custom message type for VM_INFO
[minix3.git] / drivers / tda19988 / tda19988.c
blob25176d10aa0bb1651e2377b03129461a2c50b67b
1 #include <minix/blockdriver.h>
2 #include <minix/drivers.h>
3 #include <minix/ds.h>
4 #include <minix/i2c.h>
5 #include <minix/i2cdriver.h>
6 #include <minix/log.h>
8 #include <ctype.h>
9 #include <stdio.h>
10 #include <stdlib.h>
12 /* constants */
13 #define NR_DEVS 1 /* number of devices this driver handles */
14 #define TDA19988_DEV 0 /* index of TDA19988 device */
15 #define EDID_LEN 128 /* length of standard EDID block */
17 /* When passing data over a grant one needs to pass
18 * a buffer to sys_safecopy copybuff is used for that*/
19 #define COPYBUF_SIZE 0x1000 /* 4k buf */
20 static unsigned char copybuf[COPYBUF_SIZE];
22 /* The device has two I2C interfaces CEC (0x34) and HDMI (0x70). This driver
23 * needs access to both.
27 * CEC - Register and Bit Definitions
30 #define CEC_STATUS_REG 0xfe
31 #define CEC_STATUS_CONNECTED_MASK 0x02
33 #define CEC_ENABLE_REG 0xff
34 #define CEC_ENABLE_ALL_MASK 0x87
37 * HDMI - Pages
39 * The HDMI part is much bigger than the CEC part. Memory is accessed according
40 * to page and address. Once the page is set, only the address needs to be
41 * sent if accessing memory locations within the same page (you don't need to
42 * send the page number every time).
45 #define HDMI_CTRL_PAGE 0x00
46 #define HDMI_PPL_PAGE 0x02
47 #define HDMI_EDID_PAGE 0x09
48 #define HDMI_INFO_PAGE 0x10
49 #define HDMI_AUDIO_PAGE 0x11
50 #define HDMI_HDCP_OTP_PAGE 0x12
51 #define HDMI_GAMUT_PAGE 0x13
53 /*
54 * The page select register isn't part of a page. A dummy value of 0xff is
55 * used to signfiy this in the code.
57 #define HDMI_PAGELESS 0xff
60 * Control Page Registers and Bit Definitions
63 #define HDMI_CTRL_REV_LO_REG 0x00
64 #define HDMI_CTRL_REV_HI_REG 0x02
66 #define HDMI_CTRL_RESET_REG 0x0a
67 #define HDMI_CTRL_RESET_DDC_MASK 0x02
69 #define HDMI_CTRL_DDC_CTRL_REG 0x0b
70 #define HDMI_CTRL_DDC_EN_MASK 0x00
72 #define HDMI_CTRL_DDC_CLK_REG 0x0c
73 #define HDMI_CTRL_DDC_CLK_EN_MASK 0x01
75 #define HDMI_CTRL_INTR_CTRL_REG 0x0f
76 #define HDMI_CTRL_INTR_EN_GLO_MASK 0x04
78 #define HDMI_CTRL_INT_REG 0x11
79 #define HDMI_CTRL_INT_EDID_MASK 0x02
82 * EDID Page Registers and Bit Definitions
85 #define HDMI_EDID_DATA_REG 0x00
87 #define HDMI_EDID_DEV_ADDR_REG 0xfb
88 #define HDMI_EDID_DEV_ADDR 0xa0
90 #define HDMI_EDID_OFFSET_REG 0xfc
91 #define HDMI_EDID_OFFSET 0x00
93 #define HDMI_EDID_SEG_PTR_ADDR_REG 0xfc
94 #define HDMI_EDID_SEG_PTR_ADDR 0x00
96 #define HDMI_EDID_SEG_ADDR_REG 0xfe
97 #define HDMI_EDID_SEG_ADDR 0x00
99 #define HDMI_EDID_REQ_REG 0xfa
100 #define HDMI_EDID_REQ_READ_MASK 0x01
103 * HDCP and OTP
105 #define HDMI_HDCP_OTP_DDC_CLK_REG 0x9a
106 #define HDMI_HDCP_OTP_DDC_CLK_MASK 0x27
108 /* this register/mask isn't documented but it has to be cleared/set */
109 #define HDMI_HDCP_OTP_SOME_REG 0x9b
110 #define HDMI_HDCP_OTP_SOME_MASK 0x02
113 * Pageless Registers
116 #define HDMI_PAGE_SELECT_REG 0xff
119 * Constants
122 /* Revision of the TDA19988. */
123 #define HDMI_REV_TDA19988 0x0331
125 /* the bus that this device is on (counting starting at 1) */
126 static uint32_t cec_bus;
127 static uint32_t hdmi_bus;
129 /* slave address of the device */
130 static i2c_addr_t cec_address;
131 static i2c_addr_t hdmi_address;
133 /* endpoint for the driver for the bus itself. */
134 static endpoint_t cec_bus_endpoint;
135 static endpoint_t hdmi_bus_endpoint;
137 /* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */
138 static struct log log = {
139 .name = "tda19988",
140 .log_level = LEVEL_INFO,
141 .log_func = default_log
144 static void sef_local_startup(void);
145 static int sef_cb_lu_state_save(int);
146 static int lu_state_restore(void);
147 static int sef_cb_init(int type, sef_init_info_t * info);
149 /* CEC Module */
150 static int is_display_connected(void);
151 static int enable_hdmi_module(void);
153 /* HDMI Module */
154 static int set_page(uint8_t page);
155 static int hdmi_read(uint8_t page, uint8_t reg, uint8_t * val);
156 static int hdmi_write(uint8_t page, uint8_t reg, uint8_t val);
157 static int hdmi_set(uint8_t page, uint8_t reg, uint8_t mask);
158 static int hdmi_clear(uint8_t page, uint8_t reg, uint8_t mask);
160 static int hdmi_ddc_enable(void);
161 static int hdmi_init(void);
162 static int check_revision(void);
163 static int read_edid(uint8_t * data, size_t count);
165 /* libblockdriver callbacks */
166 static int tda19988_blk_open(devminor_t minor, int access);
167 static int tda19988_blk_close(devminor_t minor);
168 static ssize_t tda19988_blk_transfer(devminor_t minor, int do_write, u64_t pos,
169 endpoint_t endpt, iovec_t * iov, unsigned int count, int flags);
170 static int tda19988_blk_ioctl(devminor_t minor, unsigned long request,
171 endpoint_t endpt, cp_grant_id_t grant, endpoint_t user_endpt);
172 static struct device *tda19988_blk_part(devminor_t minor);
173 static void tda19988_blk_other(message * m, int ipc_status);
175 /* Entry points into the device dependent code of block drivers. */
176 struct blockdriver tda19988_tab = {
177 .bdr_type = BLOCKDRIVER_TYPE_OTHER,
178 .bdr_open = tda19988_blk_open,
179 .bdr_close = tda19988_blk_close,
180 .bdr_transfer = tda19988_blk_transfer,
181 .bdr_ioctl = tda19988_blk_ioctl, /* always returns ENOTTY */
182 .bdr_part = tda19988_blk_part,
183 .bdr_other = tda19988_blk_other /* for notify events from DS */
186 /* counts the number of times a device file is open */
187 static int openct[NR_DEVS];
189 /* base and size of each device */
190 static struct device geom[NR_DEVS];
192 static int
193 tda19988_blk_open(devminor_t minor, int access)
195 log_trace(&log, "tda19988_blk_open(%d,%d)\n", minor, access);
196 if (tda19988_blk_part(minor) == NULL) {
197 return ENXIO;
200 openct[minor]++;
202 return OK;
205 static int
206 tda19988_blk_close(devminor_t minor)
208 log_trace(&log, "tda19988_blk_close(%d)\n", minor);
209 if (tda19988_blk_part(minor) == NULL) {
210 return ENXIO;
213 if (openct[minor] < 1) {
214 log_warn(&log, "closing unopened device %d\n", minor);
215 return EINVAL;
217 openct[minor]--;
219 return OK;
222 static ssize_t
223 tda19988_blk_transfer(devminor_t minor, int do_write, u64_t pos64,
224 endpoint_t endpt, iovec_t * iov, unsigned int nr_req, int flags)
226 unsigned count;
227 struct device *dv;
228 u64_t dv_size;
229 int r;
230 cp_grant_id_t grant;
232 log_trace(&log, "tda19988_blk_transfer()\n");
234 /* Get minor device information. */
235 dv = tda19988_blk_part(minor);
236 if (dv == NULL) {
237 return ENXIO;
240 if (nr_req > NR_IOREQS) {
241 return EINVAL;
244 dv_size = dv->dv_size;
245 if (pos64 >= dv_size) {
246 return OK; /* Beyond EOF */
249 if (nr_req > 0) {
251 /* How much to transfer and where to / from. */
252 count = iov->iov_size;
253 grant = (cp_grant_id_t) iov->iov_addr;
255 /* check for EOF */
256 if (pos64 >= dv_size) {
257 return 0;
260 /* don't go past the end of the device */
261 if (pos64 + count > dv_size) {
262 count = dv_size - pos64;
265 /* don't overflow copybuf */
266 if (count > COPYBUF_SIZE) {
267 count = COPYBUF_SIZE;
270 log_debug(&log, "transfering 0x%x bytes\n", count);
272 if (do_write) {
274 log_warn(&log, "Error: writing to read-only device\n");
275 return EACCES;
277 } else {
279 if (is_display_connected() == 1) {
281 r = hdmi_init();
282 if (r != OK) {
283 log_warn(&log,
284 "Failed to enable HDMI module\n");
285 return EIO;
288 memset(copybuf, '\0', COPYBUF_SIZE);
289 r = read_edid(copybuf, count);
290 if (r != OK) {
291 log_warn(&log,
292 "read_edid() failed (r=%d)\n", r);
293 return r;
296 r = sys_safecopyto(endpt, grant, (vir_bytes)
297 0, (vir_bytes) copybuf, count);
298 if (r != OK) {
299 log_warn(&log, "safecopyto failed\n");
300 return EINVAL;
303 return iov->iov_size;
304 } else {
305 log_warn(&log, "Display not connected.\n");
306 return ENODEV;
309 } else {
311 /* empty request */
312 return 0;
316 static int
317 tda19988_blk_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
318 cp_grant_id_t grant, endpoint_t UNUSED(user_endpt))
320 log_trace(&log, "tda19988_blk_ioctl(%d)\n", minor);
321 /* no supported ioctls for this device */
322 return ENOTTY;
325 static struct device *
326 tda19988_blk_part(devminor_t minor)
328 log_trace(&log, "tda19988_blk_part(%d)\n", minor);
330 if (minor < 0 || minor >= NR_DEVS) {
331 return NULL;
334 return &geom[minor];
337 static void
338 tda19988_blk_other(message * m, int ipc_status)
340 log_trace(&log, "tda19988_blk_other(0x%x)\n", m->m_type);
342 if (is_ipc_notify(ipc_status)) {
343 if (m->m_source == DS_PROC_NR) {
344 log_debug(&log,
345 "bus driver changed state, update endpoint\n");
346 i2cdriver_handle_bus_update(&cec_bus_endpoint, cec_bus,
347 cec_address);
348 i2cdriver_handle_bus_update(&hdmi_bus_endpoint,
349 hdmi_bus, hdmi_address);
351 } else {
352 log_warn(&log, "Invalid message type (0x%x)\n", m->m_type);
357 * Check to see if a display is connected.
358 * Returns 1 for yes, 0 for no, -1 for error.
360 static int
361 is_display_connected(void)
363 int r;
364 uint8_t val;
366 r = i2creg_read8(cec_bus_endpoint, cec_address, CEC_STATUS_REG, &val);
367 if (r != OK) {
368 log_warn(&log, "Reading connection status failed (r=%d)\n", r);
369 return -1;
372 if ((CEC_STATUS_CONNECTED_MASK & val) == 0) {
373 log_debug(&log, "No Display Detected\n");
374 return 0;
375 } else {
376 log_debug(&log, "Display Detected\n");
377 return 1;
382 * Enable all the modules and clocks.
384 static int
385 enable_hdmi_module(void)
387 int r;
389 r = i2creg_write8(cec_bus_endpoint, cec_address, CEC_ENABLE_REG,
390 CEC_ENABLE_ALL_MASK);
391 if (r != OK) {
392 log_warn(&log, "Writing enable bits failed (r=%d)\n", r);
393 return -1;
396 log_debug(&log, "HDMI module enabled\n");
398 return OK;
401 static int
402 set_page(uint8_t page)
404 int r;
405 static int current_page = HDMI_PAGELESS;
407 if (page != current_page) {
409 r = i2creg_write8(hdmi_bus_endpoint, hdmi_address,
410 HDMI_PAGE_SELECT_REG, page);
411 if (r != OK) {
412 return r;
415 current_page = page;
418 return OK;
421 static int
422 hdmi_read_block(uint8_t page, uint8_t reg, uint8_t * buf, size_t buflen)
425 int r;
426 minix_i2c_ioctl_exec_t ioctl_exec;
428 if (buf == NULL || buflen > I2C_EXEC_MAX_BUFLEN) {
429 log_warn(&log,
430 "Read block called with NULL pointer or invalid buflen.\n");
431 return EINVAL;
434 if (page != HDMI_PAGELESS) {
435 r = set_page(page);
436 if (r != OK) {
437 log_warn(&log, "Unable to set page to 0x%x\n", page);
438 return r;
442 memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t));
444 /* Read from HDMI */
445 ioctl_exec.iie_op = I2C_OP_READ_WITH_STOP;
446 ioctl_exec.iie_addr = hdmi_address;
448 /* write the register address */
449 ioctl_exec.iie_cmd[0] = reg;
450 ioctl_exec.iie_cmdlen = 1;
452 /* read bytes */
453 ioctl_exec.iie_buflen = buflen;
455 r = i2cdriver_exec(hdmi_bus_endpoint, &ioctl_exec);
456 if (r != OK) {
457 log_warn(&log, "hdmi_read() failed (r=%d)\n", r);
458 return -1;
461 memcpy(buf, ioctl_exec.iie_buf, buflen);
463 log_trace(&log, "Read %d bytes from reg 0x%x in page 0x%x\n", buflen,
464 reg, page);
466 return OK;
469 static int
470 hdmi_read(uint8_t page, uint8_t reg, uint8_t * val)
473 int r;
475 if (val == NULL) {
476 log_warn(&log, "Read called with NULL pointer\n");
477 return EINVAL;
480 if (page != HDMI_PAGELESS) {
481 r = set_page(page);
482 if (r != OK) {
483 log_warn(&log, "Unable to set page to 0x%x\n", page);
484 return r;
488 r = i2creg_read8(hdmi_bus_endpoint, hdmi_address, reg, val);
489 if (r != OK) {
490 log_warn(&log, "hdmi_read() failed (r=%d)\n", r);
491 return -1;
494 log_trace(&log, "Read 0x%x from reg 0x%x in page 0x%x\n", *val, reg,
495 page);
497 return OK;
500 static int
501 hdmi_write(uint8_t page, uint8_t reg, uint8_t val)
503 int r;
505 if (page != HDMI_PAGELESS) {
506 r = set_page(page);
507 if (r != OK) {
508 log_warn(&log, "Unable to set page to 0x%x\n", page);
509 return r;
513 r = i2creg_write8(hdmi_bus_endpoint, hdmi_address, reg, val);
514 if (r != OK) {
515 log_warn(&log, "hdmi_write() failed (r=%d)\n", r);
516 return -1;
519 log_trace(&log, "Successfully wrote 0x%x to reg 0x%x in page 0x%x\n",
520 val, reg, page);
522 return OK;
525 static int
526 hdmi_set(uint8_t page, uint8_t reg, uint8_t mask)
529 int r;
530 uint8_t val;
532 val = 0x00;
534 r = hdmi_read(page, reg, &val);
535 if (r != OK) {
536 return r;
539 val |= mask;
541 r = hdmi_write(page, reg, val);
542 if (r != OK) {
543 return r;
546 return OK;
549 static int
550 hdmi_clear(uint8_t page, uint8_t reg, uint8_t mask)
553 int r;
554 uint8_t val;
556 val = 0x00;
558 r = hdmi_read(page, reg, &val);
559 if (r != OK) {
560 return r;
563 val &= ~mask;
565 r = hdmi_write(page, reg, val);
566 if (r != OK) {
567 return r;
570 return OK;
573 static int
574 check_revision(void)
576 int r;
577 uint8_t rev_lo;
578 uint8_t rev_hi;
579 uint16_t revision;
581 r = hdmi_read(HDMI_CTRL_PAGE, HDMI_CTRL_REV_LO_REG, &rev_lo);
582 if (r != OK) {
583 log_warn(&log, "Failed to read rev_lo (r=%d)\n", r);
584 return -1;
587 r = hdmi_read(HDMI_CTRL_PAGE, HDMI_CTRL_REV_HI_REG, &rev_hi);
588 if (r != OK) {
589 log_warn(&log, "Failed to read rev_hi (r=%d)\n", r);
590 return -1;
593 revision = ((rev_hi << 8) | rev_lo);
594 if (revision != HDMI_REV_TDA19988) {
596 log_warn(&log, "Unrecognized value in revision registers.\n");
597 log_warn(&log, "Read: 0x%x | Expected: 0x%x\n", revision,
598 HDMI_REV_TDA19988);
599 return -1;
602 log_debug(&log, "Device Revision: 0x%x\n", revision);
604 return OK;
607 static int
608 hdmi_ddc_enable(void)
610 int r;
612 /* Soft Reset DDC Bus */
613 r = hdmi_set(HDMI_CTRL_PAGE, HDMI_CTRL_RESET_REG,
614 HDMI_CTRL_RESET_DDC_MASK);
615 if (r != OK) {
616 return r;
618 micro_delay(100000);
619 r = hdmi_clear(HDMI_CTRL_PAGE, HDMI_CTRL_RESET_REG,
620 HDMI_CTRL_RESET_DDC_MASK);
621 if (r != OK) {
622 return r;
624 micro_delay(100000);
626 /* Enable DDC */
627 r = hdmi_write(HDMI_CTRL_PAGE, HDMI_CTRL_DDC_CTRL_REG,
628 HDMI_CTRL_DDC_EN_MASK);
629 if (r != OK) {
630 return r;
633 /* Setup the clock (I think) */
634 r = hdmi_write(HDMI_CTRL_PAGE, HDMI_CTRL_DDC_CLK_REG,
635 HDMI_CTRL_DDC_CLK_EN_MASK);
636 if (r != OK) {
637 return r;
640 r = hdmi_write(HDMI_HDCP_OTP_PAGE, HDMI_HDCP_OTP_DDC_CLK_REG,
641 HDMI_HDCP_OTP_DDC_CLK_MASK);
642 if (r != OK) {
643 return r;
645 log_debug(&log, "DDC Enabled\n");
647 return OK;
650 static int
651 hdmi_init(void)
653 int r;
655 /* Turn on HDMI module (slave 0x70) */
656 r = enable_hdmi_module();
657 if (r != OK) {
658 log_warn(&log, "HDMI Module Init Failed\n");
659 return -1;
662 /* Read chip version to ensure compatibility */
663 r = check_revision();
664 if (r != OK) {
665 log_warn(&log, "Couldn't find expected TDA19988 revision\n");
666 return -1;
669 /* Turn on DDC interface between TDA19988 and display */
670 r = hdmi_ddc_enable();
671 if (r != OK) {
672 log_warn(&log, "Failed to enable DDC\n");
673 return -1;
676 return OK;
679 static int
680 read_edid(uint8_t * buf, size_t count)
682 int r;
683 int i, j;
684 int tries;
685 int edid_ready;
686 uint8_t val;
688 log_debug(&log, "Reading edid...\n");
690 if (buf == NULL || count < EDID_LEN) {
691 log_warn(&log, "Expected 128 byte data buffer\n");
692 return -1;
695 r = hdmi_clear(HDMI_HDCP_OTP_PAGE, HDMI_HDCP_OTP_SOME_REG,
696 HDMI_HDCP_OTP_SOME_MASK);
697 if (r != OK) {
698 log_warn(&log, "Failed to clear bit in HDCP OTP reg\n");
699 return -1;
702 /* Enable EDID Block Read Interrupt */
703 r = hdmi_set(HDMI_CTRL_PAGE, HDMI_CTRL_INT_REG,
704 HDMI_CTRL_INT_EDID_MASK);
705 if (r != OK) {
706 log_warn(&log, "Failed to enable EDID Block Read interrupt\n");
707 return -1;
710 /* enable global interrupts */
711 r = hdmi_write(HDMI_CTRL_PAGE, HDMI_CTRL_INTR_CTRL_REG,
712 HDMI_CTRL_INTR_EN_GLO_MASK);
713 if (r != OK) {
714 log_warn(&log, "Failed to enable interrupts\n");
715 return -1;
718 /* Set Device Address */
719 r = hdmi_write(HDMI_EDID_PAGE, HDMI_EDID_DEV_ADDR_REG,
720 HDMI_EDID_DEV_ADDR);
721 if (r != OK) {
722 log_warn(&log, "Couldn't set device address\n");
723 return -1;
726 /* Set Offset */
727 r = hdmi_write(HDMI_EDID_PAGE, HDMI_EDID_OFFSET_REG, HDMI_EDID_OFFSET);
728 if (r != OK) {
729 log_warn(&log, "Couldn't set offset\n");
730 return -1;
733 /* Set Segment Pointer Address */
734 r = hdmi_write(HDMI_EDID_PAGE, HDMI_EDID_SEG_PTR_ADDR_REG,
735 HDMI_EDID_SEG_PTR_ADDR);
736 if (r != OK) {
737 log_warn(&log, "Couldn't set segment pointer address\n");
738 return -1;
741 /* Set Segment Address */
742 r = hdmi_write(HDMI_EDID_PAGE, HDMI_EDID_SEG_ADDR_REG,
743 HDMI_EDID_SEG_ADDR);
744 if (r != OK) {
745 log_warn(&log, "Couldn't set segment address\n");
746 return -1;
750 * Toggle EDID Read Request Bit to request a read.
753 r = hdmi_write(HDMI_EDID_PAGE, HDMI_EDID_REQ_REG,
754 HDMI_EDID_REQ_READ_MASK);
755 if (r != OK) {
756 log_warn(&log, "Couldn't set Read Request bit\n");
757 return -1;
760 r = hdmi_write(HDMI_EDID_PAGE, HDMI_EDID_REQ_REG, 0x00);
761 if (r != OK) {
762 log_warn(&log, "Couldn't clear Read Request bit\n");
763 return -1;
766 log_debug(&log, "Starting polling\n");
768 /* poll interrupt status flag */
769 edid_ready = 0;
770 for (tries = 0; tries < 100; tries++) {
772 r = hdmi_read(HDMI_CTRL_PAGE, HDMI_CTRL_INT_REG, &val);
773 if (r != OK) {
774 log_warn(&log, "Read failed while polling int flag\n");
775 return -1;
778 if (val & HDMI_CTRL_INT_EDID_MASK) {
779 log_debug(&log, "Mask Set\n");
780 edid_ready = 1;
781 break;
784 micro_delay(1000);
787 if (!edid_ready) {
788 log_warn(&log, "Data Ready interrupt never fired.\n");
789 return EBUSY;
792 log_debug(&log, "Ready to read\n");
794 /* Finally, perform the read. */
795 memset(buf, '\0', count);
796 r = hdmi_read_block(HDMI_EDID_PAGE, HDMI_EDID_DATA_REG, buf, EDID_LEN);
797 if (r != OK) {
798 log_warn(&log, "Failed to read EDID data\n");
799 return -1;
802 /* Disable EDID Block Read Interrupt */
803 r = hdmi_clear(HDMI_CTRL_PAGE, HDMI_CTRL_INT_REG,
804 HDMI_CTRL_INT_EDID_MASK);
805 if (r != OK) {
806 log_warn(&log,
807 "Failed to disable EDID Block Read interrupt\n");
808 return -1;
811 r = hdmi_set(HDMI_HDCP_OTP_PAGE, HDMI_HDCP_OTP_SOME_REG,
812 HDMI_HDCP_OTP_SOME_MASK);
813 if (r != OK) {
814 log_warn(&log, "Failed to set bit in HDCP/OTP reg\n");
815 return -1;
818 log_debug(&log, "Done EDID Reading\n");
820 return OK;
823 static int
824 sef_cb_lu_state_save(int UNUSED(state))
826 ds_publish_u32("cec_bus", cec_bus, DSF_OVERWRITE);
827 ds_publish_u32("hdmi_bus", hdmi_bus, DSF_OVERWRITE);
828 ds_publish_u32("cec_address", cec_address, DSF_OVERWRITE);
829 ds_publish_u32("hdmi_address", hdmi_address, DSF_OVERWRITE);
830 return OK;
833 static int
834 lu_state_restore(void)
836 /* Restore the state. */
837 u32_t value;
839 ds_retrieve_u32("cec_bus", &value);
840 ds_delete_u32("cec_bus");
841 cec_bus = (int) value;
843 ds_retrieve_u32("hdmi_bus", &value);
844 ds_delete_u32("hdmi_bus");
845 hdmi_bus = (int) value;
847 ds_retrieve_u32("cec_address", &value);
848 ds_delete_u32("cec_address");
849 cec_address = (int) value;
851 ds_retrieve_u32("hdmi_address", &value);
852 ds_delete_u32("hdmi_address");
853 hdmi_address = (int) value;
855 return OK;
858 static int
859 sef_cb_init(int type, sef_init_info_t * UNUSED(info))
861 int r;
863 if (type == SEF_INIT_LU) {
864 /* Restore the state. */
865 lu_state_restore();
868 geom[TDA19988_DEV].dv_base = ((u64_t) (0));
869 geom[TDA19988_DEV].dv_size = ((u64_t) (128));
872 * CEC Module
875 /* look-up the endpoint for the bus driver */
876 cec_bus_endpoint = i2cdriver_bus_endpoint(cec_bus);
877 if (cec_bus_endpoint == 0) {
878 log_warn(&log, "Couldn't find bus driver.\n");
879 return EXIT_FAILURE;
882 /* claim the device */
883 r = i2cdriver_reserve_device(cec_bus_endpoint, cec_address);
884 if (r != OK) {
885 log_warn(&log, "Couldn't reserve device 0x%x (r=%d)\n",
886 cec_address, r);
887 return EXIT_FAILURE;
891 * HDMI Module
894 /* look-up the endpoint for the bus driver */
895 hdmi_bus_endpoint = i2cdriver_bus_endpoint(hdmi_bus);
896 if (hdmi_bus_endpoint == 0) {
897 log_warn(&log, "Couldn't find bus driver.\n");
898 return EXIT_FAILURE;
901 /* claim the device */
902 r = i2cdriver_reserve_device(hdmi_bus_endpoint, hdmi_address);
903 if (r != OK) {
904 log_warn(&log, "Couldn't reserve device 0x%x (r=%d)\n",
905 hdmi_address, r);
906 return EXIT_FAILURE;
909 if (type != SEF_INIT_LU) {
911 /* sign up for updates about the i2c bus going down/up */
912 r = i2cdriver_subscribe_bus_updates(cec_bus);
913 if (r != OK) {
914 log_warn(&log, "Couldn't subscribe to bus updates\n");
915 return EXIT_FAILURE;
918 /* sign up for updates about the i2c bus going down/up */
919 r = i2cdriver_subscribe_bus_updates(hdmi_bus);
920 if (r != OK) {
921 log_warn(&log, "Couldn't subscribe to bus updates\n");
922 return EXIT_FAILURE;
925 i2cdriver_announce(cec_bus);
926 if (cec_bus != hdmi_bus) {
927 i2cdriver_announce(hdmi_bus);
930 blockdriver_announce(type);
931 log_trace(&log, "announced\n");
934 return OK;
937 static void
938 sef_local_startup(void)
941 * Register init callbacks. Use the same function for all event types
943 sef_setcb_init_fresh(sef_cb_init);
944 sef_setcb_init_lu(sef_cb_init);
945 sef_setcb_init_restart(sef_cb_init);
948 * Register live update callbacks.
950 /* Agree to update immediately when LU is requested in a valid state. */
951 sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
952 /* Support live update starting from any standard state. */
953 sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);
954 /* Register a custom routine to save the state. */
955 sef_setcb_lu_state_save(sef_cb_lu_state_save);
957 /* Let SEF perform startup. */
958 sef_startup();
961 static int
962 tda19988_env_parse()
964 int r;
965 long int cec_busl;
966 long int cec_addressl;
967 long int hdmi_busl;
968 long int hdmi_addressl;
970 r = env_parse("cec_bus", "d", 0, &cec_busl, 1, 3);
971 if (r != EP_SET) {
972 return -1;
974 cec_bus = (uint32_t) cec_busl;
976 r = env_parse("cec_address", "x", 0, &cec_addressl, 0x34, 0x37);
977 if (r != EP_SET) {
978 return -1;
980 cec_address = (i2c_addr_t) cec_addressl;
982 r = env_parse("hdmi_bus", "d", 0, &hdmi_busl, 1, 3);
983 if (r != EP_SET) {
984 return -1;
986 hdmi_bus = (uint32_t) hdmi_busl;
988 r = env_parse("hdmi_address", "x", 0, &hdmi_addressl, 0x70, 0x73);
989 if (r != EP_SET) {
990 return -1;
992 hdmi_address = (i2c_addr_t) hdmi_addressl;
994 return OK;
998 main(int argc, char *argv[])
1000 int r;
1002 env_setargs(argc, argv);
1004 r = tda19988_env_parse();
1005 if (r < 0) {
1006 log_warn(&log,
1007 "Expecting -args 'cec_bus=X cec_address=0xAA hdmi_bus=Y hdmi_address=0xBB'\n");
1008 log_warn(&log,
1009 "Example -args 'cec_bus=1 cec_address=0x34 hdmi_bus=1 hdmi_address=0x70'\n");
1010 return EXIT_FAILURE;
1013 sef_local_startup();
1015 log_debug(&log, "Startup Complete\n");
1016 blockdriver_task(&tda19988_tab);
1017 log_debug(&log, "Shutting down\n");
1019 return OK;