c7457320b0c1b94dff2dcd2a1b12b7ff362c11cb
[firefly-linux-kernel-4.4.55.git] / drivers / video / rockchip / hdmi / rockchip-hdmi-cec.c
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"
13
14 static struct cec_device *cec_dev;
15 struct input_dev *devinput;
16 static struct miscdevice mdev;
17
18 int key_table[] = {
19         KEY_UP,
20         KEY_DOWN,
21         KEY_LEFT,
22         KEY_RIGHT,
23         KEY_REPLY,
24         KEY_BACK,
25         KEY_POWER,
26 };
27
28 static void cecmenucontrol(int uitemp);
29
30 static int cecreadframe(struct cec_framedata *frame)
31 {
32         if (frame == NULL || !cec_dev || cec_dev->readframe == NULL)
33                 return -1;
34         else
35                 return cec_dev->readframe(cec_dev->hdmi, frame);
36 }
37
38 static int cecsendframe(struct cec_framedata *frame)
39 {
40         if (frame == NULL || !cec_dev || cec_dev->readframe == NULL)
41                 return -1;
42         else
43                 return cec_dev->sendframe(cec_dev->hdmi, frame);
44 }
45
46 static int cecsendping(char logicaddress)
47 {
48         struct cec_framedata cecframe;
49
50         memset(&cecframe, 0, sizeof(struct cec_framedata));
51         cecframe.srcdestaddr = logicaddress << 4 | logicaddress;
52         return cec_dev->sendframe(cec_dev->hdmi, &cecframe);
53 }
54
55 /*static int CecSendMessage (char opCode, char dest)
56 {
57         struct cec_framedata cecframe;
58
59         cecframe.opcode        = opCode;
60         cecframe.srcdestaddr   = MAKE_SRCDEST(cec_dev->address_logic, dest);
61         cecframe.argcount      = 0;
62
63         return cecsendframe(&cecframe);
64 }*/
65
66
67 /*static void CecSendFeatureAbort (struct cec_framedata *pcpi, char reason)
68 {
69         struct cec_framedata cecframe;
70
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);
79         }
80 }*/
81
82 static void cecsendimageview(void)
83 {
84          struct cec_framedata cecframe;
85
86          cecframe.opcode        = CECOP_IMAGE_VIEW_ON;
87          cecframe.srcdestaddr   = MAKE_SRCDEST(cec_dev->address_logic,
88                                                CEC_LOGADDR_TV);
89          cecframe.argcount      = 0;
90          cecsendframe(&cecframe);
91 }
92
93 static void cecsendactivesource(void)
94 {
95         struct cec_framedata cecframe;
96
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);
104 }
105
106 static void cechandleinactivesource(struct cec_framedata *pcpi)
107 {
108 }
109
110 static void cechandlefeatureabort(struct cec_framedata *pcpi)
111 {
112 }
113
114 static bool validatececmessage(struct cec_framedata *pcpi)
115 {
116         char parametercount = 0;
117         bool    countok = true;
118
119         /* Determine required parameter count   */
120
121         switch (pcpi->opcode) {
122         case CECOP_IMAGE_VIEW_ON:
123         case CECOP_TEXT_VIEW_ON:
124         case CECOP_STANDBY:
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:
129                 parametercount = 0;
130                 break;
131         case CECOP_REPORT_POWER_STATUS:         /* power status*/
132         case CECOP_CEC_VERSION:                 /* cec version*/
133                 parametercount = 1;
134                 break;
135         case CECOP_INACTIVE_SOURCE:             /* physical address*/
136         case CECOP_FEATURE_ABORT:
137         case CECOP_ACTIVE_SOURCE:               /* physical address*/
138                 parametercount = 2;
139                 break;
140         case CECOP_REPORT_PHYSICAL_ADDRESS:
141         case CECOP_DEVICE_VENDOR_ID:            /* vendor id*/
142                 parametercount = 3;
143                 break;
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*/
147                 break;
148         case CECOP_ABORT:
149                 break;
150         case CECOP_ARC_INITIATE:
151                 break;
152         case CECOP_ARC_REPORT_INITIATED:
153                 break;
154         case CECOP_ARC_REPORT_TERMINATED:
155                 break;
156         case CECOP_ARC_REQUEST_INITIATION:
157                 break;
158         case CECOP_ARC_REQUEST_TERMINATION:
159                 break;
160         case CECOP_ARC_TERMINATE:
161                 break;
162         default:
163                 break;
164         }
165
166         /* Test for correct parameter count.    */
167
168         if (pcpi->argcount < parametercount)
169                 countok = false;
170
171         return countok;
172 }
173
174 static bool cecrxmsghandlerlast(struct cec_framedata *pcpi)
175 {
176         bool                    isdirectaddressed;
177         struct cec_framedata    cecframe;
178
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]);
188                         break;
189
190                 case CECOP_VENDOR_REMOTE_BUTTON_DOWN:
191                         cecmenucontrol(pcpi->args[0]);
192                         break;
193                 case CECOP_FEATURE_ABORT:
194                         cechandlefeatureabort(pcpi);
195                         break;
196
197                 case CECOP_GIVE_OSD_NAME:
198                         cecframe.opcode        = CECOP_SET_OSD_NAME;
199                         cecframe.srcdestaddr =
200                                 MAKE_SRCDEST(cec_dev->address_logic,
201                                              CEC_LOGADDR_TV);
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);
210                         break;
211
212                 case CECOP_VENDOR_COMMAND_WITH_ID:
213
214                 if (pcpi->args[2] == 00) {
215                         cecframe.opcode        = CECOP_SET_OSD_NAME;
216                         cecframe.srcdestaddr =
217                                 MAKE_SRCDEST(cec_dev->address_logic,
218                                              CEC_LOGADDR_TV);
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);
227                         }
228                         break;
229                 case CECOP_IMAGE_VIEW_ON:
230                 case CECOP_TEXT_VIEW_ON:
231                 /* In our case, respond the same to both these messages*/
232                     break;
233
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);
244                         break;
245
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;
253                         break;
254
255                 case CECOP_INACTIVE_SOURCE:
256                         cechandleinactivesource(pcpi);
257                         break;
258
259                 case CECOP_GIVE_PHYSICAL_ADDRESS:
260
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);
270                         break;
271
272                 case CECOP_GIVE_DEVICE_POWER_STATUS:
273                 /* TV responds with power status.   */
274
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);
283                         break;
284
285                 case CECOP_GET_MENU_LANGUAGE:
286                 /* TV Responds with a Set Menu language command.    */
287
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);
297                         break;
298
299                 case CECOP_GET_CEC_VERSION:
300                 /* TV responds to this request with it's CEC version support.*/
301
302                         cecframe.srcdestaddr   =
303                                 MAKE_SRCDEST(cec_dev->address_logic,
304                                              CEC_LOGADDR_TV);
305                         cecframe.opcode        = CECOP_CEC_VERSION;
306                         cecframe.args[0]       = 0x05;       /* Report CEC1.4b*/
307                         cecframe.argcount      = 1;
308                         cecsendframe(&cecframe);
309                         break;
310
311                 case CECOP_REPORT_POWER_STATUS:
312                 /*Someone sent us their power state.
313
314                         l_sourcePowerStatus = pcpi->args[0];
315
316                         let NEW SOURCE task know about it.
317
318                         if ( l_cecTaskState.task == SI_CECTASK_NEWSOURCE )
319                         {
320                         l_cecTaskState.cpiState = CPI_RESPONSE;
321                         }*/
322                          break;
323
324                 /* Do not reply to directly addressed 'Broadcast' msgs.  */
325                 case CECOP_REQUEST_ACTIVE_SOURCE:
326                         cecsendactivesource();
327                         break;
328
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:
336                         break;
337
338                 case CECOP_ABORT:
339                         break;
340                 default:
341                 /*CecSendFeatureAbort(pcpi, CECAR_UNRECOG_OPCODE);*/
342                         break;
343                         }
344                 } else {
345                         /* Respond to broadcast messages.   */
346                         switch (pcpi->opcode) {
347                         case CECOP_STANDBY:
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);
358                                 break;
359
360                         case CECOP_ACTIVE_SOURCE:
361                                 /*CecHandleActiveSource( pcpi );*/
362                                 break;
363
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);
373                                 break;
374
375                 /* Do not reply to 'Broadcast' msgs that we don't need.*/
376                         case CECOP_REQUEST_ACTIVE_SOURCE:
377                                 cecsendactivesource();
378                                 break;
379                         case CECOP_ROUTING_CHANGE:
380                         case CECOP_ROUTING_INFORMATION:
381                         case CECOP_SET_STREAM_PATH:
382                         case CECOP_SET_MENU_LANGUAGE:
383                                 break;
384                         }
385                 }
386         }
387
388         return 0;
389 }
390
391 static void cecenumeration(void)
392 {
393         char logicaddress[3] = {CEC_LOGADDR_PLAYBACK1,
394                                 CEC_LOGADDR_PLAYBACK2,
395                                 CEC_LOGADDR_PLAYBACK3};
396         int i;
397         int trynum;
398         int rtvalue;
399         int availablecnt;
400
401         if (!cec_dev)
402                 return;
403
404         for (i = 0; i < 3; i++) {
405                 rtvalue = 0;
406                 availablecnt = 0;
407                 for (trynum = 0; trynum < 3; trynum++) {
408                         rtvalue = cecsendping(logicaddress[i]);
409                         if (rtvalue == 1) {
410                                 availablecnt++;
411                                 CECDBG("availablecnt: %d\n", availablecnt);
412                          }
413                          mdelay(5);
414                 }
415                 if (availablecnt > 1) {
416                         cec_dev->address_logic = logicaddress[i];
417                         CECDBG("logic address is 0x%x\n",
418                                cec_dev->address_logic);
419                         break;
420                 }
421         }
422         if (i == 3)
423                 cec_dev->address_logic = CEC_LOGADDR_UNREGORBC;
424         cec_dev->setceclogicaddr(cec_dev->hdmi, cec_dev->address_logic);
425         cecsendimageview();
426         cecsendactivesource();
427 }
428
429 static void cecworkfunc(struct work_struct *work)
430 {
431         struct cec_delayed_work *cec_w =
432                 container_of(work, struct cec_delayed_work, work.work);
433         struct cec_framedata cecframe;
434
435         switch (cec_w->event) {
436         case EVENT_ENUMERATE:
437                 cecenumeration();
438                 break;
439         case EVENT_RX_FRAME:
440                 memset(&cecframe, 0, sizeof(struct cec_framedata));
441                 cecreadframe(&cecframe);
442                 cecrxmsghandlerlast(&cecframe);
443                 break;
444         default:
445                 break;
446         }
447
448         kfree(cec_w->data);
449         kfree(cec_w);
450 }
451
452 void rockchip_hdmi_cec_submit_work(int event, int delay, void *data)
453 {
454         struct cec_delayed_work *work;
455
456         CECDBG("%s event %04x delay %d\n", __func__, event, delay);
457
458         work = kmalloc(sizeof(*work), GFP_ATOMIC);
459
460         if (work) {
461                 INIT_DELAYED_WORK(&work->work, cecworkfunc);
462                 work->event = event;
463                 work->data = data;
464                 queue_delayed_work(cec_dev->workqueue,
465                                    &work->work,
466                                    msecs_to_jiffies(delay));
467         } else {
468                 CECDBG(KERN_WARNING "CEC: Cannot allocate memory\n");
469         }
470 }
471
472 void rockchip_hdmi_cec_set_pa(int devpa)
473 {
474         if (cec_dev)
475                 cec_dev->address_phy = devpa;
476         cecenumeration();
477 }
478
479 static int cec_input_device_init(void)
480 {
481         int err, i;
482
483         devinput = input_allocate_device();
484          if (!devinput)
485                 return -ENOMEM;
486         devinput->name = "hdmi_cec_key";
487         /*devinput->dev.parent = &client->dev;*/
488         devinput->phys = "hdmi_cec_key/input0";
489         devinput->id.bustype = BUS_HOST;
490         devinput->id.vendor = 0x0001;
491         devinput->id.product = 0x0001;
492         devinput->id.version = 0x0100;
493         err = input_register_device(devinput);
494         if (err < 0) {
495                 input_free_device(devinput);
496                 CECDBG("%s input device error", __func__);
497                 return err;
498         }
499         for (i = 0; i < (sizeof(key_table)/sizeof(int)); i++)
500                 input_set_capability(devinput, EV_KEY, key_table[i]);
501         return 0;
502 }
503
504 static void cecmenucontrol(int uitemp)
505 {
506         switch (uitemp) {
507         case S_CEC_MAKESURE:  /*make sure*/
508                 CECDBG("CEC UIcommand  makesure\n");
509                 input_event(devinput, EV_KEY, KEY_REPLY, 1);
510                 input_sync(devinput);
511                 input_event(devinput, EV_KEY, KEY_REPLY, 0);
512                 input_sync(devinput);
513                 break;
514         case S_CEC_UP:  /*up*/
515                 CECDBG("CEC UIcommand  up\n");
516                 input_event(devinput, EV_KEY, KEY_UP, 1);
517                 input_sync(devinput);
518                 input_event(devinput, EV_KEY, KEY_UP, 0);
519                 input_sync(devinput);
520                 break;
521         case S_CEC_DOWN:  /*down*/
522                 CECDBG("CEC UIcommand  down\n");
523                 input_event(devinput, EV_KEY, KEY_DOWN, 1);
524                 input_sync(devinput);
525                 input_event(devinput, EV_KEY, KEY_DOWN, 0);
526                 input_sync(devinput);
527                 break;
528         case S_CEC_LEFT:  /*left*/
529                 CECDBG("CEC UIcommand  left\n");
530                 input_event(devinput, EV_KEY, KEY_LEFT , 1);
531                 input_sync(devinput);
532                 input_event(devinput, EV_KEY, KEY_LEFT , 0);
533                 input_sync(devinput);
534                 break;
535         case S_CEC_RIGHT:  /*right*/
536                 CECDBG("CEC UIcommand  right\n");
537                 input_event(devinput, EV_KEY, KEY_RIGHT, 1);
538                 input_sync(devinput);
539                 input_event(devinput, EV_KEY, KEY_RIGHT, 0);
540                 input_sync(devinput);
541                 break;
542         case S_CEC_BACK:  /*back*/
543                 CECDBG("CEC UIcommand  back\n");
544                 input_event(devinput, EV_KEY, KEY_BACK, 1);
545                 input_sync(devinput);
546                 input_event(devinput, EV_KEY, KEY_BACK, 0);
547                 input_sync(devinput);
548                 break;
549         case S_CEC_VENDORBACK:
550                 CECDBG("CEC UIcommand  vendor back\n");
551                 input_event(devinput, EV_KEY, KEY_BACK, 1);
552                 input_sync(devinput);
553                 input_event(devinput, EV_KEY, KEY_BACK, 0);
554                 input_sync(devinput);
555                 break;
556         }
557 }
558
559
560 static ssize_t  cec_show(struct device *dev,
561                          struct device_attribute *attr, char *buf)
562 {
563         return snprintf(buf, PAGE_SIZE, "%s\n", cec_dev->cecval);
564 }
565
566 static ssize_t cec_store(struct device *dev,
567                          struct device_attribute *attr,
568                          const char *buf, size_t count)
569 {
570         int ret;
571
572         ret = sscanf(buf, "%s", cec_dev->cecval);
573         return strnlen(buf, PAGE_SIZE);
574 }
575
576 static struct device_attribute cec_control_attr = {
577         .attr = {.name = "cec", .mode = 0666},
578         .show = cec_show,
579         .store = cec_store,
580 };
581
582 int rockchip_hdmi_cec_init(struct hdmi *hdmi,
583                            int (*sendframe)(struct hdmi *,
584                                             struct cec_framedata *),
585                            int (*readframe)(struct hdmi *,
586                                             struct cec_framedata *),
587                            void (*setceclogicaddr)(struct hdmi *, int))
588 {
589         int ret;
590         static int cecmicsdevflag = 1;
591
592         mdev.minor = MISC_DYNAMIC_MINOR;
593         mdev.name = "cec";
594         mdev.mode = 0666;
595         cec_dev = kmalloc(sizeof(*cec_dev), GFP_KERNEL);
596         if (!cec_dev) {
597                 pr_err("HDMI CEC: kmalloc fail!");
598                 return -ENOMEM;
599         }
600         memset(cec_dev, 0, sizeof(struct cec_device));
601         cec_dev->hdmi = hdmi;
602         cec_dev->cecval[0] = '1';
603         cec_dev->cecval[1] = '\0';
604         cec_dev->sendframe = sendframe;
605         cec_dev->readframe = readframe;
606         cec_dev->setceclogicaddr = setceclogicaddr;
607         cec_dev->workqueue = create_singlethread_workqueue("hdmi-cec");
608         if (cec_dev->workqueue == NULL) {
609                 pr_err("HDMI CEC: create workqueue failed.\n");
610                 return -1;
611         }
612         if (cecmicsdevflag) {
613                 cec_input_device_init();
614         if (misc_register(&mdev)) {
615                 pr_err("CEC: Could not add cec misc driver\n");
616                 goto error;
617         }
618
619         ret = device_create_file(mdev.this_device, &cec_control_attr);
620         if (ret) {
621                 pr_err("CEC: Could not add sys file enable\n");
622         goto error1;
623         }
624         cecmicsdevflag = 0;
625         }
626         return 0;
627
628 error1:
629                 misc_deregister(&mdev);
630 error:
631                 ret = -EINVAL;
632         return ret;
633 }