tps65217: driver for the TPS65217 PMIC
Change-Id: Ic2259c15645816627d757c9c45560cb4c5c0156c
This commit is contained in:
parent
a9f194e58f
commit
d9b62047f1
7 changed files with 568 additions and 3 deletions
|
@ -112,5 +112,6 @@
|
|||
./usr/sbin/lan8710a minix-sys
|
||||
./usr/sbin/random minix-sys
|
||||
./usr/sbin/tda19988 minix-sys
|
||||
./usr/sbin/tps65217 minix-sys
|
||||
./usr/tests/minix-posix/mod minix-sys
|
||||
./usr/tests/minix-posix/test63 minix-sys
|
||||
|
|
|
@ -23,7 +23,8 @@ SUBDIR= ahci amddev atl2 at_wini audio dec21140A dp8390 dpeth \
|
|||
.endif
|
||||
|
||||
.if ${MACHINE_ARCH} == "earm"
|
||||
SUBDIR= cat24c256 fb gpio i2c mmc log readclock tda19988 tty random lan8710a
|
||||
SUBDIR= cat24c256 fb gpio i2c mmc lan8710a log readclock \
|
||||
tda19988 tps65217 tty random
|
||||
.endif
|
||||
|
||||
.endif # ${MKIMAGEONLY} != "yes"
|
||||
|
|
14
drivers/tps65217/Makefile
Normal file
14
drivers/tps65217/Makefile
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Makefile for the tps65217 Power Management IC found on the BeagleBones
|
||||
PROG= tps65217
|
||||
SRCS= tps65217.c
|
||||
|
||||
DPADD+= ${LIBI2CDRIVER} ${CLKCONF} ${LIBSYS} ${LIBTIMERS}
|
||||
LDADD+= -li2cdriver -lclkconf -lsys -ltimers
|
||||
|
||||
MAN=
|
||||
|
||||
BINDIR?= /usr/sbin
|
||||
|
||||
CPPFLAGS+= -I${NETBSDSRCDIR}
|
||||
|
||||
.include <minix.service.mk>
|
20
drivers/tps65217/README.txt
Normal file
20
drivers/tps65217/README.txt
Normal file
|
@ -0,0 +1,20 @@
|
|||
TPS65217 Driver (Power Management IC)
|
||||
=====================================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
This driver is for the power management chip commonly found on the BeagleBone
|
||||
and the BeagleBone Black.
|
||||
|
||||
Testing the Code
|
||||
----------------
|
||||
|
||||
Starting up an instance:
|
||||
|
||||
/bin/service up /usr/sbin/tps65217 -label tps65217.1.24 \
|
||||
-args 'bus=1 address=0x24'
|
||||
|
||||
Killing an instance:
|
||||
|
||||
/bin/service down tps65217.1.24
|
511
drivers/tps65217/tps65217.c
Normal file
511
drivers/tps65217/tps65217.c
Normal file
|
@ -0,0 +1,511 @@
|
|||
#include <minix/ds.h>
|
||||
#include <minix/drivers.h>
|
||||
#include <minix/i2c.h>
|
||||
#include <minix/i2cdriver.h>
|
||||
#include <minix/log.h>
|
||||
#include <minix/reboot.h>
|
||||
|
||||
/* Register Addresses */
|
||||
#define CHIPID_REG 0x00
|
||||
#define PPATH_REG 0x01
|
||||
#define INT_REG 0x02
|
||||
#define CHGCONFIG0_REG 0x03
|
||||
#define CHGCONFIG1_REG 0x04
|
||||
#define CHGCONFIG2_REG 0x05
|
||||
#define CHGCONFIG3_REG 0x06
|
||||
#define WLEDCTRL1_REG 0x07
|
||||
#define WLEDCTRL2_REG 0x08
|
||||
#define MUXCTRL_REG 0x09
|
||||
#define STATUS_REG 0x0a
|
||||
#define PASSWORD_REG 0x0b
|
||||
#define PGOOD_REG 0x0c
|
||||
#define DEFPG_REG 0x0d
|
||||
#define DEFDCDC1_REG 0x0e
|
||||
#define DEFDCDC2_REG 0x0f
|
||||
#define DEFDCDC3_REG 0x10
|
||||
#define DEFSLEW_REG 0x11
|
||||
#define DEFLDO1_REG 0x12
|
||||
#define DEFLDO2_REG 0x13
|
||||
#define DEFLS1_REG 0x14
|
||||
#define DEFLS2_REG 0x15
|
||||
#define ENABLE_REG 0x16
|
||||
/* no documented register at 0x17 */
|
||||
#define DEFUVLO_REG 0x18
|
||||
#define SEQ1_REG 0x19
|
||||
#define SEQ2_REG 0x1a
|
||||
#define SEQ3_REG 0x1b
|
||||
#define SEQ4_REG 0x1c
|
||||
#define SEQ5_REG 0x1d
|
||||
#define SEQ6_REG 0x1e
|
||||
|
||||
/* Bits and Masks */
|
||||
|
||||
/*
|
||||
* CHIP masks - CHIPID_REG[7:4]
|
||||
*/
|
||||
#define TPS65217A_CHIP_MASK 0x70
|
||||
#define TPS65217B_CHIP_MASK 0xf0
|
||||
#define TPS65217C_CHIP_MASK 0xe0
|
||||
#define TPS65217D_CHIP_MASK 0x60
|
||||
|
||||
/*
|
||||
* Interrupt Enable/Disable Bits/Masks - INT_REG[6:4]
|
||||
* 0=Enable 1=Disable | Default mask: Disable ACM, USBM ~ Enable only PBM
|
||||
*/
|
||||
#define PBM_INT_DIS_BIT 6
|
||||
#define ACM_INT_DIS_BIT 5
|
||||
#define USBM_INT_DIS_BIT 4
|
||||
#define DEFAULT_INT_MASK ((1<<ACM_INT_DIS_BIT)|(1<<USBM_INT_DIS_BIT))
|
||||
|
||||
/*
|
||||
* Interrupt Status Bits - INT_REG[3:0]
|
||||
*/
|
||||
#define PBI_BIT 2
|
||||
#define ACI_BIT 1
|
||||
#define USBI_BIT 0
|
||||
#define PBI_MASK (1<<PBI_BIT)
|
||||
|
||||
/*
|
||||
* Power Off Bit - STATUS[7]
|
||||
*/
|
||||
#define OFF_BIT 7
|
||||
#define PWR_OFF_MASK (1<<OFF_BIT)
|
||||
|
||||
/* The TPS65217 is connected to the NMI pin of the AM335X on the BeagleBone and
|
||||
* BeagleBone Black. That line is used to signal to the SoC that an interrupt
|
||||
* has happened in the TPS65217. The NMI pin in turn generates an interrupt
|
||||
* in the SoC which this driver will receive.
|
||||
*/
|
||||
static int irq = 7;
|
||||
static int irq_hook_id = 7;
|
||||
static int irq_hook_kernel_id = 7;
|
||||
|
||||
/* Only valid slave address for this device is 0x24 */
|
||||
static i2c_addr_t valid_addrs[2] = {
|
||||
0x24, 0x00
|
||||
};
|
||||
|
||||
/* the bus that this device is on (counting starting at 1) */
|
||||
static uint32_t bus;
|
||||
|
||||
/* slave address of the device */
|
||||
static i2c_addr_t address;
|
||||
|
||||
/* endpoint for the driver for the bus itself. */
|
||||
static endpoint_t bus_endpoint;
|
||||
|
||||
/* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */
|
||||
static struct log log = {
|
||||
.name = "tps65217",
|
||||
.log_level = LEVEL_INFO,
|
||||
.log_func = default_log
|
||||
};
|
||||
|
||||
/* Register Access */
|
||||
static int reg_read(uint8_t reg, uint8_t * val);
|
||||
static int reg_write(uint8_t reg, uint8_t val);
|
||||
|
||||
/* Device Specific Functions */
|
||||
static int check_revision(void);
|
||||
static int enable_pwr_off(void);
|
||||
static int intr_enable(void);
|
||||
static int intr_handler(void);
|
||||
static void do_shutdown(int how);
|
||||
|
||||
/* SEF Related Function Prototypes */
|
||||
static void sef_local_startup(void);
|
||||
static int sef_cb_lu_state_save(int);
|
||||
static int lu_state_restore(void);
|
||||
static int sef_cb_init(int type, sef_init_info_t * info);
|
||||
|
||||
static int
|
||||
reg_read(uint8_t reg, uint8_t * val)
|
||||
{
|
||||
int r;
|
||||
minix_i2c_ioctl_exec_t ioctl_exec;
|
||||
|
||||
if (val == NULL) {
|
||||
log_warn(&log, "Read called with NULL pointer\n");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t));
|
||||
|
||||
/* Read from chip */
|
||||
ioctl_exec.iie_op = I2C_OP_READ_WITH_STOP;
|
||||
ioctl_exec.iie_addr = address;
|
||||
|
||||
/* write the register address */
|
||||
ioctl_exec.iie_cmd[0] = reg;
|
||||
ioctl_exec.iie_cmdlen = 1;
|
||||
|
||||
/* read 1 byte */
|
||||
ioctl_exec.iie_buflen = 1;
|
||||
|
||||
r = i2cdriver_exec(bus_endpoint, &ioctl_exec);
|
||||
if (r != OK) {
|
||||
log_warn(&log, "reg_read() failed (r=%d)\n", r);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*val = ioctl_exec.iie_buf[0];
|
||||
|
||||
log_trace(&log, "Read 0x%x from reg 0x%x", *val, reg);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int
|
||||
reg_write(uint8_t reg, uint8_t val)
|
||||
{
|
||||
int r;
|
||||
minix_i2c_ioctl_exec_t ioctl_exec;
|
||||
|
||||
if (reg >= 0x0d) {
|
||||
/* TODO: writes to password protected registers hasn't
|
||||
* been implemented since nothing in this driver needs to
|
||||
* write to them. When needed, it should be implemented.
|
||||
*/
|
||||
log_warn(&log, "Cannot write to protected registers.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t));
|
||||
|
||||
/* Write to chip */
|
||||
ioctl_exec.iie_op = I2C_OP_WRITE_WITH_STOP;
|
||||
ioctl_exec.iie_addr = address;
|
||||
|
||||
/* write the register address and value */
|
||||
ioctl_exec.iie_buf[0] = reg;
|
||||
ioctl_exec.iie_buf[1] = val;
|
||||
ioctl_exec.iie_buflen = 2;
|
||||
|
||||
r = i2cdriver_exec(bus_endpoint, &ioctl_exec);
|
||||
if (r != OK) {
|
||||
log_warn(&log, "reg_write() failed (r=%d)\n", r);
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_trace(&log, "Successfully wrote 0x%x to reg 0x%x\n", val, reg);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int
|
||||
check_revision(void)
|
||||
{
|
||||
int r;
|
||||
uint8_t chipid;
|
||||
|
||||
r = reg_read(CHIPID_REG, &chipid);
|
||||
if (r != OK) {
|
||||
log_warn(&log, "Failed to read CHIPID\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (chipid & 0xf0) {
|
||||
case TPS65217A_CHIP_MASK:
|
||||
log_debug(&log, "TPS65217A rev 1.%d\n", (chipid & 0x0f));
|
||||
break;
|
||||
case TPS65217B_CHIP_MASK:
|
||||
log_debug(&log, "TPS65217B rev 1.%d\n", (chipid & 0x0f));
|
||||
break;
|
||||
case TPS65217C_CHIP_MASK:
|
||||
log_debug(&log, "TPS65217C rev 1.%d\n", (chipid & 0x0f));
|
||||
break;
|
||||
case TPS65217D_CHIP_MASK:
|
||||
log_debug(&log, "TPS65217D rev 1.%d\n", (chipid & 0x0f));
|
||||
break;
|
||||
default:
|
||||
log_warn(&log, "Unexpected CHIPID: 0x%x\n", chipid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int
|
||||
enable_pwr_off(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
/* enable power off via the PWR_EN pin. just do the setup here.
|
||||
* the kernel will do the work to toggle the pin when the
|
||||
* system is ready to be powered off. Should be called during startup
|
||||
* so that shutdown(8) can do power-off with reboot(RBT_POWEROFF).
|
||||
*/
|
||||
r = reg_write(STATUS_REG, PWR_OFF_MASK);
|
||||
if (r != OK) {
|
||||
log_warn(&log, "Cannot set power off mask.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
intr_enable(void)
|
||||
{
|
||||
int r;
|
||||
uint8_t val;
|
||||
static int policy_set = 0;
|
||||
static int irq_enabled = 0;
|
||||
|
||||
/* Enable IRQ */
|
||||
if (!policy_set) {
|
||||
r = sys_irqsetpolicy(irq, 0, &irq_hook_kernel_id);
|
||||
if (r == OK) {
|
||||
policy_set = 1;
|
||||
} else {
|
||||
log_warn(&log, "Couldn't set irq policy\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (policy_set && !irq_enabled) {
|
||||
r = sys_irqenable(&irq_hook_kernel_id);
|
||||
if (r == OK) {
|
||||
irq_enabled = 1;
|
||||
} else {
|
||||
log_warn(&log, "Couldn't enable irq %d (hooked)\n",
|
||||
irq);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable/Disable interrupts in the TPS65217 */
|
||||
r = reg_write(INT_REG, DEFAULT_INT_MASK);
|
||||
if (r != OK) {
|
||||
log_warn(&log, "Failed to set interrupt mask.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Read from the interrupt register to clear any pending interrupts */
|
||||
r = reg_read(INT_REG, &val);
|
||||
if (r != OK) {
|
||||
log_warn(&log, "Failed to read interrupt register.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int
|
||||
intr_handler(void)
|
||||
{
|
||||
int r;
|
||||
uint8_t val;
|
||||
struct tm t;
|
||||
|
||||
/* read interrupt register to get interrupt that fired and clear it */
|
||||
r = reg_read(INT_REG, &val);
|
||||
if (r != OK) {
|
||||
log_warn(&log, "Failed to read interrupt register.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((val & PBI_MASK) != 0) {
|
||||
log_info(&log, "Power Button Pressed\n");
|
||||
reboot(RBT_POWEROFF);
|
||||
log_warn(&log, "Failed to power off the system.");
|
||||
sys_irqenable(&irq_hook_kernel_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* re-enable interrupt */
|
||||
r = sys_irqenable(&irq_hook_kernel_id);
|
||||
if (r != OK) {
|
||||
log_warn(&log, "Unable to renable IRQ (r=%d)\n", r);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int
|
||||
sef_cb_lu_state_save(int UNUSED(state))
|
||||
{
|
||||
ds_publish_u32("bus", bus, DSF_OVERWRITE);
|
||||
ds_publish_u32("address", address, DSF_OVERWRITE);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int
|
||||
lu_state_restore(void)
|
||||
{
|
||||
/* Restore the state. */
|
||||
u32_t value;
|
||||
|
||||
ds_retrieve_u32("bus", &value);
|
||||
ds_delete_u32("bus");
|
||||
bus = (int) value;
|
||||
|
||||
ds_retrieve_u32("address", &value);
|
||||
ds_delete_u32("address");
|
||||
address = (int) value;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int
|
||||
sef_cb_init(int type, sef_init_info_t * UNUSED(info))
|
||||
{
|
||||
int r;
|
||||
|
||||
if (type == SEF_INIT_LU) {
|
||||
/* Restore the state. */
|
||||
lu_state_restore();
|
||||
}
|
||||
|
||||
/* look-up the endpoint for the bus driver */
|
||||
bus_endpoint = i2cdriver_bus_endpoint(bus);
|
||||
if (bus_endpoint == 0) {
|
||||
log_warn(&log, "Couldn't find bus driver.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* claim the device */
|
||||
r = i2cdriver_reserve_device(bus_endpoint, address);
|
||||
if (r != OK) {
|
||||
log_warn(&log, "Couldn't reserve device 0x%x (r=%d)\n",
|
||||
address, r);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* check that the chip / rev is reasonable */
|
||||
r = check_revision();
|
||||
if (r != OK) {
|
||||
/* prevent user from using the driver with a different chip */
|
||||
log_warn(&log, "Bad CHIPID\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* enable interrupts */
|
||||
r = intr_enable();
|
||||
if (r != OK) {
|
||||
log_warn(&log, "Failed to enable interrupts.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* enable power-off pin so the kernel can cut power to the SoC */
|
||||
enable_pwr_off();
|
||||
|
||||
if (type != SEF_INIT_LU) {
|
||||
|
||||
/* sign up for updates about the i2c bus going down/up */
|
||||
r = i2cdriver_subscribe_bus_updates(bus);
|
||||
if (r != OK) {
|
||||
log_warn(&log, "Couldn't subscribe to bus updates\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
i2cdriver_announce(bus);
|
||||
log_debug(&log, "announced\n");
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static void
|
||||
sef_local_startup(void)
|
||||
{
|
||||
/*
|
||||
* Register init callbacks. Use the same function for all event types
|
||||
*/
|
||||
sef_setcb_init_fresh(sef_cb_init);
|
||||
sef_setcb_init_lu(sef_cb_init);
|
||||
sef_setcb_init_restart(sef_cb_init);
|
||||
|
||||
/*
|
||||
* Register live update callbacks.
|
||||
*/
|
||||
/* Agree to update immediately when LU is requested in a valid state. */
|
||||
sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
|
||||
/* Support live update starting from any standard state. */
|
||||
sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);
|
||||
/* Register a custom routine to save the state. */
|
||||
sef_setcb_lu_state_save(sef_cb_lu_state_save);
|
||||
|
||||
/* Let SEF perform startup. */
|
||||
sef_startup();
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int r;
|
||||
endpoint_t user, caller;
|
||||
message m;
|
||||
int ipc_status;
|
||||
|
||||
env_setargs(argc, argv);
|
||||
|
||||
r = i2cdriver_env_parse(&bus, &address, valid_addrs);
|
||||
if (r < 0) {
|
||||
log_warn(&log, "Expecting -args 'bus=X address=0xYY'\n");
|
||||
log_warn(&log, "Example -args 'bus=1 address=0x24'\n");
|
||||
return EXIT_FAILURE;
|
||||
} else if (r > 0) {
|
||||
log_warn(&log,
|
||||
"Invalid slave address for device, expecting 0x24\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
sef_local_startup();
|
||||
|
||||
while (TRUE) {
|
||||
|
||||
/* Receive Message */
|
||||
r = sef_receive_status(ANY, &m, &ipc_status);
|
||||
if (r != OK) {
|
||||
log_warn(&log, "sef_receive_status() failed\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
log_trace(&log, "Got a message 0x%x from 0x%x\n", m.m_type,
|
||||
m.m_source);
|
||||
|
||||
if (is_ipc_notify(ipc_status)) {
|
||||
|
||||
switch (m.m_source) {
|
||||
|
||||
case DS_PROC_NR:
|
||||
/* bus driver changed state, update endpoint */
|
||||
i2cdriver_handle_bus_update(&bus_endpoint, bus,
|
||||
address);
|
||||
break;
|
||||
case HARDWARE:
|
||||
intr_handler();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Do not reply to notifications. */
|
||||
continue;
|
||||
}
|
||||
|
||||
caller = m.m_source;
|
||||
user = m.USER_ENDPT;
|
||||
|
||||
/*
|
||||
* Handle Message
|
||||
*
|
||||
* So far this driver only deals with notifications
|
||||
* so it always replies to non-notifications with EINVAL.
|
||||
*/
|
||||
|
||||
/* Send Reply */
|
||||
m.m_type = TASK_REPLY;
|
||||
m.REP_ENDPT = user;
|
||||
m.REP_STATUS = EINVAL;
|
||||
|
||||
r = sendnb(caller, &m);
|
||||
if (r != OK) {
|
||||
log_warn(&log, "sendnb() failed\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -621,6 +621,14 @@ service tda19988
|
|||
ipc SYSTEM RS DS i2c;
|
||||
};
|
||||
|
||||
service tps65217
|
||||
{
|
||||
uid 0; # needed for doing reboot(RBT_POWEROFF)
|
||||
system IRQCTL PRIVCTL;
|
||||
irq 7; # NNMI pin on BeagleBone / BeagleBone Black
|
||||
ipc SYSTEM RS DS PM i2c;
|
||||
};
|
||||
|
||||
service vbox
|
||||
{
|
||||
system
|
||||
|
|
14
etc/usr/rc
14
etc/usr/rc
|
@ -202,7 +202,13 @@ start)
|
|||
echo -n "Starting i2c device drivers: "
|
||||
test -e /dev/eepromb1s50 || (cd /dev && MAKEDEV eepromb1s50)
|
||||
up cat24c256 -dev /dev/eepromb1s50 \
|
||||
-label cat24c256.1.50 -args 'bus=1 address=0x50'
|
||||
-label cat24c256.1.50 \
|
||||
-args 'bus=1 address=0x50'
|
||||
|
||||
# Start TPS65217 driver for power management.
|
||||
up tps65217 -label tps65217.1.24 \
|
||||
-args 'bus=1 address=0x24'
|
||||
|
||||
;;
|
||||
A335BNLT)
|
||||
echo "Detected BeagleBone Black"
|
||||
|
@ -211,7 +217,11 @@ start)
|
|||
up cat24c256 -dev /dev/eepromb1s50 \
|
||||
-label cat24c256.1.50 -args 'bus=1 address=0x50'
|
||||
|
||||
# Start TDA19988 driver for EDID reading.
|
||||
# Start TPS65217 driver for power management.
|
||||
up tps65217 -label tps65217.1.24 \
|
||||
-args 'bus=1 address=0x24'
|
||||
|
||||
# Start TDA19988 driver for reading EDID.
|
||||
up tda19988 -label tda19988.1.3470 -args \
|
||||
'cec_bus=1 cec_address=0x34 hdmi_bus=1 hdmi_address=0x70'
|
||||
;;
|
||||
|
|
Loading…
Reference in a new issue