1 #include <linux/kernel.h>
2 #include <linux/slab.h>
3 #include <linux/workqueue.h>
4 #include <linux/delay.h>
5 #include <linux/module.h>
6 #include <linux/kernel.h>
7 #include <linux/errno.h>
8 #include <linux/string.h>
9 #include <linux/miscdevice.h>
10 #include <linux/workqueue.h>
11 #include <linux/firmware.h>
12 #include "rockchip-hdmi-cec.h"
14 static struct cec_device *cec_dev;
15 struct input_dev *devinput;
16 static struct miscdevice mdev;
28 static void cecmenucontrol(int uitemp);
30 static int cecreadframe(struct cec_framedata *frame)
32 if (frame == NULL || !cec_dev || cec_dev->readframe == NULL)
35 return cec_dev->readframe(cec_dev->hdmi, frame);
38 static int cecsendframe(struct cec_framedata *frame)
40 if (frame == NULL || !cec_dev || cec_dev->readframe == NULL)
43 return cec_dev->sendframe(cec_dev->hdmi, frame);
46 static int cecsendping(char logicaddress)
48 struct cec_framedata cecframe;
50 memset(&cecframe, 0, sizeof(struct cec_framedata));
51 cecframe.srcdestaddr = logicaddress << 4 | logicaddress;
52 return cec_dev->sendframe(cec_dev->hdmi, &cecframe);
55 /*static int CecSendMessage (char opCode, char dest)
57 struct cec_framedata cecframe;
59 cecframe.opcode = opCode;
60 cecframe.srcdestaddr = MAKE_SRCDEST(cec_dev->address_logic, dest);
61 cecframe.argcount = 0;
63 return cecsendframe(&cecframe);
67 /*static void CecSendFeatureAbort (struct cec_framedata *pcpi, char reason)
69 struct cec_framedata cecframe;
71 if ((pcpi->srcdestaddr & 0x0F) != CEC_LOGADDR_UNREGORBC) {
72 cecframe.opcode = CECOP_FEATURE_ABORT;
73 cecframe.srcdestaddr = MAKE_SRCDEST( cec_dev->address_logic,
74 ( pcpi->srcdestaddr & 0xF0) >> 4 );
75 cecframe.args[0] = pcpi->opcode;
76 cecframe.args[1] = reason;
77 cecframe.argcount = 2;
78 cecsendframe(&cecframe);
82 static void cecsendimageview(void)
84 struct cec_framedata cecframe;
86 cecframe.opcode = CECOP_IMAGE_VIEW_ON;
87 cecframe.srcdestaddr = MAKE_SRCDEST(cec_dev->address_logic,
88 CEC_LOGADDR_UNREGORBC);
89 cecframe.argcount = 0;
90 cecsendframe(&cecframe);
93 static void cecsendactivesource(void)
95 struct cec_framedata cecframe;
97 cecframe.opcode = CECOP_ACTIVE_SOURCE;
98 cecframe.srcdestaddr = MAKE_SRCDEST(cec_dev->address_logic,
99 CEC_LOGADDR_UNREGORBC);
100 cecframe.args[0] = (cec_dev->address_phy & 0xFF00) >> 8;
101 cecframe.args[1] = (cec_dev->address_phy & 0x00FF);
102 cecframe.argcount = 2;
103 cecsendframe(&cecframe);
106 static void cechandleinactivesource(struct cec_framedata *pcpi)
110 static void cechandlefeatureabort(struct cec_framedata *pcpi)
114 static bool validatececmessage(struct cec_framedata *pcpi)
116 char parametercount = 0;
119 /* Determine required parameter count */
121 switch (pcpi->opcode) {
122 case CECOP_IMAGE_VIEW_ON:
123 case CECOP_TEXT_VIEW_ON:
125 case CECOP_GIVE_PHYSICAL_ADDRESS:
126 case CECOP_GIVE_DEVICE_POWER_STATUS:
127 case CECOP_GET_MENU_LANGUAGE:
128 case CECOP_GET_CEC_VERSION:
131 case CECOP_REPORT_POWER_STATUS: /* power status*/
132 case CECOP_CEC_VERSION: /* cec version*/
135 case CECOP_INACTIVE_SOURCE: /* physical address*/
136 case CECOP_FEATURE_ABORT:
137 case CECOP_ACTIVE_SOURCE: /* physical address*/
140 case CECOP_REPORT_PHYSICAL_ADDRESS:
141 case CECOP_DEVICE_VENDOR_ID: /* vendor id*/
144 case CECOP_SET_OSD_NAME: /* osd name (1-14 bytes)*/
145 case CECOP_SET_OSD_STRING:
146 parametercount = 1; /* must have a minimum of 1 operands*/
150 case CECOP_ARC_INITIATE:
152 case CECOP_ARC_REPORT_INITIATED:
154 case CECOP_ARC_REPORT_TERMINATED:
156 case CECOP_ARC_REQUEST_INITIATION:
158 case CECOP_ARC_REQUEST_TERMINATION:
160 case CECOP_ARC_TERMINATE:
166 /* Test for correct parameter count. */
168 if (pcpi->argcount < parametercount)
174 static bool cecrxmsghandlerlast(struct cec_framedata *pcpi)
176 bool isdirectaddressed;
177 struct cec_framedata cecframe;
179 isdirectaddressed = !((pcpi->srcdestaddr & 0x0F) ==
180 CEC_LOGADDR_UNREGORBC);
181 pr_info("isDirectAddressed %d\n", (int)isdirectaddressed);
182 if (validatececmessage(pcpi)) {
183 /* If invalid message, ignore it, but treat it as handled */
184 if (isdirectaddressed) {
185 switch (pcpi->opcode) {
186 case CECOP_USER_CONTROL_PRESSED:
187 cecmenucontrol(pcpi->args[0]);
190 case CECOP_VENDOR_REMOTE_BUTTON_DOWN:
191 cecmenucontrol(pcpi->args[0]);
193 case CECOP_FEATURE_ABORT:
194 cechandlefeatureabort(pcpi);
197 case CECOP_GIVE_OSD_NAME:
198 cecframe.opcode = CECOP_SET_OSD_NAME;
199 cecframe.srcdestaddr =
200 MAKE_SRCDEST(cec_dev->address_logic,
202 cecframe.args[0] = 'r';
203 cecframe.args[1] = 'k';
204 cecframe.args[2] = '-';
205 cecframe.args[3] = 'b';
206 cecframe.args[4] = 'o';
207 cecframe.args[5] = 'x';
208 cecframe.argcount = 6;
209 cecsendframe(&cecframe);
212 case CECOP_VENDOR_COMMAND_WITH_ID:
214 if (pcpi->args[2] == 00) {
215 cecframe.opcode = CECOP_SET_OSD_NAME;
216 cecframe.srcdestaddr =
217 MAKE_SRCDEST(cec_dev->address_logic,
219 cecframe.args[0] = '1';
220 cecframe.args[1] = '1';
221 cecframe.args[2] = '1';
222 cecframe.args[3] = '1';
223 cecframe.args[4] = '1';
224 cecframe.args[5] = '1';
225 cecframe.argcount = 6;
226 cecsendframe(&cecframe);
229 case CECOP_IMAGE_VIEW_ON:
230 case CECOP_TEXT_VIEW_ON:
231 /* In our case, respond the same to both these messages*/
234 case CECOP_GIVE_DEVICE_VENDOR_ID:
235 cecframe.opcode = CECOP_DEVICE_VENDOR_ID;
236 cecframe.srcdestaddr =
237 MAKE_SRCDEST(cec_dev->address_logic,
238 CEC_LOGADDR_UNREGORBC);
239 cecframe.args[0] = 0x1;
240 cecframe.args[1] = 0x2;
241 cecframe.args[2] = 0x3;
242 cecframe.argcount = 3;
243 cecsendframe(&cecframe);
246 case CECOP_STANDBY: /* Direct and Broadcast*/
247 /* Setting this here will let the main task know */
248 /* (via SI_CecGetPowerState) and at the same time */
249 /* prevent us from broadcasting a STANDBY message */
250 /* of our own when the main task responds by */
251 /* calling SI_CecSetPowerState( STANDBY ); */
252 cec_dev->powerstatus = CEC_POWERSTATUS_STANDBY;
255 case CECOP_INACTIVE_SOURCE:
256 cechandleinactivesource(pcpi);
259 case CECOP_GIVE_PHYSICAL_ADDRESS:
261 cecframe.opcode = CECOP_REPORT_PHYSICAL_ADDRESS;
262 cecframe.srcdestaddr =
263 MAKE_SRCDEST(cec_dev->address_logic,
264 CEC_LOGADDR_UNREGORBC);
265 cecframe.args[0] = (cec_dev->address_phy&0xFF00)>>8;
266 cecframe.args[1] = (cec_dev->address_phy&0x00FF);
267 cecframe.args[2] = cec_dev->address_logic;
268 cecframe.argcount = 3;
269 cecsendframe(&cecframe);
272 case CECOP_GIVE_DEVICE_POWER_STATUS:
273 /* TV responds with power status. */
275 cecframe.opcode = CECOP_REPORT_POWER_STATUS;
276 cecframe.srcdestaddr =
277 MAKE_SRCDEST(cec_dev->address_logic,
278 (pcpi->srcdestaddr & 0xF0) >> 4);
279 cec_dev->powerstatus = 0x00;
280 cecframe.args[0] = cec_dev->powerstatus;
281 cecframe.argcount = 1;
282 cecsendframe(&cecframe);
285 case CECOP_GET_MENU_LANGUAGE:
286 /* TV Responds with a Set Menu language command. */
288 cecframe.opcode = CECOP_SET_MENU_LANGUAGE;
289 cecframe.srcdestaddr =
290 MAKE_SRCDEST(cec_dev->address_logic,
291 CEC_LOGADDR_UNREGORBC);
292 cecframe.args[0] = 'e';
293 cecframe.args[1] = 'n';
294 cecframe.args[2] = 'g';
295 cecframe.argcount = 3;
296 cecsendframe(&cecframe);
299 case CECOP_GET_CEC_VERSION:
300 /* TV responds to this request with it's CEC version support.*/
302 cecframe.srcdestaddr =
303 MAKE_SRCDEST(cec_dev->address_logic,
305 cecframe.opcode = CECOP_CEC_VERSION;
306 cecframe.args[0] = 0x05; /* Report CEC1.4b*/
307 cecframe.argcount = 1;
308 cecsendframe(&cecframe);
311 case CECOP_REPORT_POWER_STATUS:
312 /*Someone sent us their power state.
314 l_sourcePowerStatus = pcpi->args[0];
316 let NEW SOURCE task know about it.
318 if ( l_cecTaskState.task == SI_CECTASK_NEWSOURCE )
320 l_cecTaskState.cpiState = CPI_RESPONSE;
324 /* Do not reply to directly addressed 'Broadcast' msgs. */
325 case CECOP_REQUEST_ACTIVE_SOURCE:
326 cecsendactivesource();
329 case CECOP_ACTIVE_SOURCE:
330 case CECOP_REPORT_PHYSICAL_ADDRESS:
331 case CECOP_ROUTING_CHANGE:
332 case CECOP_ROUTING_INFORMATION:
333 case CECOP_SET_STREAM_PATH:
334 case CECOP_SET_MENU_LANGUAGE:
335 case CECOP_DEVICE_VENDOR_ID:
341 /*CecSendFeatureAbort(pcpi, CECAR_UNRECOG_OPCODE);*/
345 /* Respond to broadcast messages. */
346 switch (pcpi->opcode) {
348 /* Setting this here will let the main task know */
349 /* (via SI_CecGetPowerState) and at the same time */
350 /* prevent us from broadcasting a STANDBY message */
351 /* of our own when the main task responds by */
352 /* calling SI_CecSetPowerState( STANDBY ); */
353 cec_dev->powerstatus = CEC_POWERSTATUS_STANDBY;
354 input_event(devinput, EV_KEY, KEY_POWER, 1);
355 input_sync(devinput);
356 input_event(devinput, EV_KEY, KEY_POWER, 0);
357 input_sync(devinput);
360 case CECOP_ACTIVE_SOURCE:
361 /*CecHandleActiveSource( pcpi );*/
364 case CECOP_REPORT_PHYSICAL_ADDRESS:
365 /*CecHandleReportPhysicalAddress( pcpi );*/
366 cecframe.srcdestaddr =
367 MAKE_SRCDEST(cec_dev->address_logic,
368 CEC_LOGADDR_UNREGORBC);
369 cecframe.opcode = CECOP_CEC_VERSION;
370 cecframe.args[0] = 0x05; /* CEC1.4b*/
371 cecframe.argcount = 1;
372 cecsendframe(&cecframe);
375 /* Do not reply to 'Broadcast' msgs that we don't need.*/
376 case CECOP_REQUEST_ACTIVE_SOURCE:
377 cecsendactivesource();
379 case CECOP_ROUTING_CHANGE:
380 case CECOP_ROUTING_INFORMATION:
381 case CECOP_SET_STREAM_PATH:
382 case CECOP_SET_MENU_LANGUAGE:
391 static void cecenumeration(void)
393 char logicaddress[3] = {CEC_LOGADDR_PLAYBACK1,
394 CEC_LOGADDR_PLAYBACK2,
395 CEC_LOGADDR_PLAYBACK3};
401 for (i = 0; i < 3; i++) {
402 if (cecsendping(logicaddress[i])) {
403 cec_dev->address_logic = logicaddress[i];
404 CECDBG("Logic Address is 0x%x\n",
405 cec_dev->address_logic);
410 cec_dev->address_logic = CEC_LOGADDR_UNREGORBC;
411 cec_dev->setceclogicaddr(cec_dev->hdmi, cec_dev->address_logic);
413 cecsendactivesource();
416 static void cecworkfunc(struct work_struct *work)
418 struct cec_delayed_work *cec_w =
419 container_of(work, struct cec_delayed_work, work.work);
420 struct cec_framedata cecframe;
422 switch (cec_w->event) {
423 case EVENT_ENUMERATE:
427 memset(&cecframe, 0, sizeof(struct cec_framedata));
428 cecreadframe(&cecframe);
429 cecrxmsghandlerlast(&cecframe);
439 void rockchip_hdmi_cec_submit_work(int event, int delay, void *data)
441 struct cec_delayed_work *work;
443 CECDBG("%s event %04x delay %d\n", __func__, event, delay);
445 work = kmalloc(sizeof(*work), GFP_ATOMIC);
448 INIT_DELAYED_WORK(&work->work, cecworkfunc);
451 queue_delayed_work(cec_dev->workqueue,
453 msecs_to_jiffies(delay));
455 CECDBG(KERN_WARNING "CEC: Cannot allocate memory\n");
459 void rockchip_hdmi_cec_set_pa(int devpa)
462 cec_dev->address_phy = devpa;
466 static int cec_input_device_init(void)
470 devinput = input_allocate_device();
473 devinput->name = "hdmi_cec_key";
474 /*devinput->dev.parent = &client->dev;*/
475 devinput->phys = "hdmi_cec_key/input0";
476 devinput->id.bustype = BUS_HOST;
477 devinput->id.vendor = 0x0001;
478 devinput->id.product = 0x0001;
479 devinput->id.version = 0x0100;
480 err = input_register_device(devinput);
482 input_free_device(devinput);
483 CECDBG("%s input device error", __func__);
486 for (i = 0; i < (sizeof(key_table)/sizeof(int)); i++)
487 input_set_capability(devinput, EV_KEY, key_table[i]);
491 static void cecmenucontrol(int uitemp)
494 case S_CEC_MAKESURE: /*make sure*/
495 CECDBG("CEC UIcommand makesure\n");
496 input_event(devinput, EV_KEY, KEY_REPLY, 1);
497 input_sync(devinput);
498 input_event(devinput, EV_KEY, KEY_REPLY, 0);
499 input_sync(devinput);
501 case S_CEC_UP: /*up*/
502 CECDBG("CEC UIcommand up\n");
503 input_event(devinput, EV_KEY, KEY_UP, 1);
504 input_sync(devinput);
505 input_event(devinput, EV_KEY, KEY_UP, 0);
506 input_sync(devinput);
508 case S_CEC_DOWN: /*down*/
509 CECDBG("CEC UIcommand down\n");
510 input_event(devinput, EV_KEY, KEY_DOWN, 1);
511 input_sync(devinput);
512 input_event(devinput, EV_KEY, KEY_DOWN, 0);
513 input_sync(devinput);
515 case S_CEC_LEFT: /*left*/
516 CECDBG("CEC UIcommand left\n");
517 input_event(devinput, EV_KEY, KEY_LEFT , 1);
518 input_sync(devinput);
519 input_event(devinput, EV_KEY, KEY_LEFT , 0);
520 input_sync(devinput);
522 case S_CEC_RIGHT: /*right*/
523 CECDBG("CEC UIcommand right\n");
524 input_event(devinput, EV_KEY, KEY_RIGHT, 1);
525 input_sync(devinput);
526 input_event(devinput, EV_KEY, KEY_RIGHT, 0);
527 input_sync(devinput);
529 case S_CEC_BACK: /*back*/
530 CECDBG("CEC UIcommand back\n");
531 input_event(devinput, EV_KEY, KEY_BACK, 1);
532 input_sync(devinput);
533 input_event(devinput, EV_KEY, KEY_BACK, 0);
534 input_sync(devinput);
536 case S_CEC_VENDORBACK:
537 CECDBG("CEC UIcommand vendor back\n");
538 input_event(devinput, EV_KEY, KEY_BACK, 1);
539 input_sync(devinput);
540 input_event(devinput, EV_KEY, KEY_BACK, 0);
541 input_sync(devinput);
547 static ssize_t cec_show(struct device *dev,
548 struct device_attribute *attr, char *buf)
550 return snprintf(buf, PAGE_SIZE, "%s\n", cec_dev->cecval);
553 static ssize_t cec_store(struct device *dev,
554 struct device_attribute *attr,
555 const char *buf, size_t count)
559 ret = sscanf(buf, "%s", cec_dev->cecval);
560 return strnlen(buf, PAGE_SIZE);
563 static struct device_attribute cec_control_attr = {
564 .attr = {.name = "cec", .mode = 0666},
569 int rockchip_hdmi_cec_init(struct hdmi *hdmi,
570 int (*sendframe)(struct hdmi *,
571 struct cec_framedata *),
572 int (*readframe)(struct hdmi *,
573 struct cec_framedata *),
574 void (*setceclogicaddr)(struct hdmi *, int))
577 static int cecmicsdevflag = 1;
579 mdev.minor = MISC_DYNAMIC_MINOR;
582 cec_dev = kmalloc(sizeof(*cec_dev), GFP_KERNEL);
584 pr_err("HDMI CEC: kmalloc fail!");
587 memset(cec_dev, 0, sizeof(struct cec_device));
588 cec_dev->hdmi = hdmi;
589 cec_dev->cecval[0] = '1';
590 cec_dev->cecval[1] = '\0';
591 cec_dev->sendframe = sendframe;
592 cec_dev->readframe = readframe;
593 cec_dev->setceclogicaddr = setceclogicaddr;
594 cec_dev->workqueue = create_singlethread_workqueue("hdmi-cec");
595 if (cec_dev->workqueue == NULL) {
596 pr_err("HDMI CEC: create workqueue failed.\n");
599 if (cecmicsdevflag) {
600 cec_input_device_init();
601 if (misc_register(&mdev)) {
602 pr_err("CEC: Could not add cec misc driver\n");
606 ret = device_create_file(mdev.this_device, &cec_control_attr);
608 pr_err("CEC: Could not add sys file enable\n");
616 misc_deregister(&mdev);