video: rockchip: dp: modify get edid method and modify get dpcd retry times.
[firefly-linux-kernel-4.4.55.git] / drivers / video / rockchip / hdmi / rockchip-hdmi-cec.c
1 #include <linux/delay.h>
2 #include <linux/errno.h>
3 #include <linux/firmware.h>
4 #include <linux/ioctl.h>
5 #include <linux/kernel.h>
6 #include <linux/module.h>
7 #include <linux/pagemap.h>
8 #include <linux/slab.h>
9 #include <linux/string.h>
10 #include <linux/workqueue.h>
11 #include "rockchip-hdmi-cec.h"
12
13 static struct cec_device *cec_dev;
14
15 static int cecreadframe(struct cec_framedata *frame)
16 {
17         int ret = -1;
18
19         if (frame && cec_dev && cec_dev->readframe && cec_dev->enable) {
20                 mutex_lock(&cec_dev->hdmi->pclk_lock);
21                 ret = cec_dev->readframe(cec_dev->hdmi, frame);
22                 mutex_unlock(&cec_dev->hdmi->pclk_lock);
23         }
24         return ret;
25 }
26
27 static int cecsendframe(struct cec_framedata *frame)
28 {
29         int ret = -1;
30
31         if (frame && cec_dev && cec_dev->sendframe) {
32                 mutex_lock(&cec_dev->hdmi->pclk_lock);
33                 ret = cec_dev->sendframe(cec_dev->hdmi, frame);
34                 mutex_unlock(&cec_dev->hdmi->pclk_lock);
35         }
36         return ret;
37 }
38
39 static void cecsetlogicaddr(int addr)
40 {
41         if (cec_dev && cec_dev->setceclogicaddr) {
42                 mutex_lock(&cec_dev->hdmi->pclk_lock);
43                 cec_dev->setceclogicaddr(cec_dev->hdmi, addr);
44                 mutex_unlock(&cec_dev->hdmi->pclk_lock);
45         }
46 }
47
48 static void cecworkfunc(struct work_struct *work)
49 {
50         struct cec_delayed_work *cec_w =
51                 container_of(work, struct cec_delayed_work, work.work);
52         struct cecframelist *list_node;
53
54         switch (cec_w->event) {
55         case EVENT_ENUMERATE:
56                 break;
57         case EVENT_RX_FRAME:
58                 list_node = kmalloc(sizeof(*list_node), GFP_KERNEL);
59                 if (!list_node)
60                         return;
61                 cecreadframe(&list_node->cecframe);
62                 if (cec_dev->enable) {
63                         mutex_lock(&cec_dev->cec_lock);
64                         list_add_tail(&list_node->framelist,
65                                       &cec_dev->ceclist);
66                         sysfs_notify(&cec_dev->device.this_device->kobj,
67                                      NULL, "stat");
68                         mutex_unlock(&cec_dev->cec_lock);
69                 } else {
70                         kfree(list_node);
71                 }
72                 break;
73         default:
74                 break;
75         }
76
77         kfree(cec_w->data);
78         kfree(cec_w);
79 }
80
81 void rockchip_hdmi_cec_submit_work(int event, int delay, void *data)
82 {
83         struct cec_delayed_work *work;
84
85         CECDBG("%s event %04x delay %d\n", __func__, event, delay);
86
87         if (!cec_dev)
88                 return;
89
90         work = kmalloc(sizeof(*work), GFP_ATOMIC);
91
92         if (work) {
93                 INIT_DELAYED_WORK(&work->work, cecworkfunc);
94                 work->event = event;
95                 work->data = data;
96                 queue_delayed_work(cec_dev->workqueue,
97                                    &work->work,
98                                    msecs_to_jiffies(delay));
99         } else {
100                 CECDBG(KERN_WARNING "CEC: Cannot allocate memory\n");
101         }
102 }
103
104 void rockchip_hdmi_cec_set_pa(int devpa)
105 {
106         struct list_head *pos, *n;
107
108         if (cec_dev) {
109                 cec_dev->address_phy = devpa;
110                 pr_info("%s %x\n", __func__, devpa);
111                 /*when hdmi hpd , ceclist will be reset*/
112                 mutex_lock(&cec_dev->cec_lock);
113                 if (!list_empty(&cec_dev->ceclist)) {
114                         list_for_each_safe(pos, n, &cec_dev->ceclist) {
115                                 list_del(pos);
116                                 kfree(pos);
117                         }
118                 }
119                 INIT_LIST_HEAD(&cec_dev->ceclist);
120                 sysfs_notify(&cec_dev->device.this_device->kobj, NULL, "stat");
121                 mutex_unlock(&cec_dev->cec_lock);
122         }
123 }
124
125 static ssize_t  cec_enable_show(struct device *dev,
126                                 struct device_attribute *attr, char *buf)
127 {
128         return snprintf(buf, PAGE_SIZE, "%d\n", cec_dev->enable);
129 }
130
131 static ssize_t cec_enable_store(struct device *dev,
132                                 struct device_attribute *attr,
133                                 const char *buf, size_t count)
134 {
135         int ret;
136
137         ret = kstrtoint(buf, 0, &cec_dev->enable);
138         return count;
139 }
140
141 static ssize_t  cec_phy_show(struct device *dev,
142                              struct device_attribute *attr, char *buf)
143 {
144         return snprintf(buf, PAGE_SIZE, "0x%x\n", cec_dev->address_phy);
145 }
146
147 static ssize_t cec_phy_store(struct device *dev,
148                              struct device_attribute *attr,
149                          const char *buf, size_t count)
150 {
151         int ret;
152
153         ret = kstrtoint(buf, 0, &cec_dev->address_phy);
154         return count;
155 }
156
157 static ssize_t  cec_logic_show(struct device *dev,
158                                struct device_attribute *attr, char *buf)
159 {
160         return snprintf(buf, PAGE_SIZE, "0x%02x\n", cec_dev->address_logic);
161 }
162
163 static ssize_t cec_logic_store(struct device *dev,
164                                struct device_attribute *attr,
165                                const char *buf, size_t count)
166 {
167         int ret;
168
169         ret = kstrtoint(buf, 0, &cec_dev->address_logic);
170         return count;
171 }
172
173 static ssize_t  cec_state_show(struct device *dev,
174                                struct device_attribute *attr, char *buf)
175 {
176         int stat;
177
178         mutex_lock(&cec_dev->cec_lock);
179         if (!cec_dev->address_phy)
180                 stat = 0;
181         else if (list_empty(&cec_dev->ceclist))
182                 stat = 1;
183         else
184                 stat = 2;
185         mutex_unlock(&cec_dev->cec_lock);
186         return snprintf(buf, PAGE_SIZE, "%d\n", stat);
187 }
188
189 static struct device_attribute cec_attrs[] = {
190         __ATTR(logic, S_IRUGO | S_IWUSR, cec_logic_show, cec_logic_store),
191         __ATTR(phy, S_IRUGO | S_IWUSR, cec_phy_show, cec_phy_store),
192         __ATTR(enable, S_IRUGO | S_IWUSR, cec_enable_show, cec_enable_store),
193         __ATTR(stat, S_IRUGO, cec_state_show, NULL),
194 };
195
196 static long cec_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
197 {
198         int ret;
199         void __user *argp;
200         struct cec_framedata cecsendtemp;
201         struct cecframelist *listemp;
202
203         argp = (void __user *)arg;
204         switch (cmd) {
205         case HDMI_IOCTL_CECSETLA:
206                 ret = copy_from_user(&cec_dev->address_logic,
207                                      argp, sizeof(int));
208                 cecsetlogicaddr(cec_dev->address_logic);
209                 break;
210         case HDMI_IOCTL_CECSEND:
211                 ret = copy_from_user(&cecsendtemp, argp,
212                                      sizeof(struct cec_framedata));
213                 ret = cecsendframe(&cecsendtemp);
214                 cecsendtemp.returnval = ret;
215                 ret = copy_to_user(argp, &cecsendtemp,
216                                    sizeof(struct cec_framedata));
217                 break;
218         case HDMI_IOCTL_CECENAB:
219                 ret = copy_from_user(&cec_dev->enable, argp, sizeof(int));
220                 break;
221         case HDMI_IOCTL_CECPHY:
222                 ret = copy_to_user(argp, &cec_dev->address_phy, sizeof(int));
223                 break;
224         case HDMI_IOCTL_CECLOGIC:
225                 ret = copy_to_user(argp, &cec_dev->address_logic,
226                                    sizeof(int));
227                 break;
228         case HDMI_IOCTL_CECREAD:
229                 mutex_lock(&cec_dev->cec_lock);
230                 if (!list_empty(&cec_dev->ceclist)) {
231                         listemp = list_entry(cec_dev->ceclist.next,
232                                              struct cecframelist, framelist);
233                         ret = copy_to_user(argp, &listemp->cecframe,
234                                            sizeof(struct cec_framedata));
235                         list_del(&listemp->framelist);
236                         kfree(listemp);
237                 }
238                 mutex_unlock(&cec_dev->cec_lock);
239                 break;
240         case HDMI_IOCTL_CECCLEARLA:
241                 break;
242         case HDMI_IOCTL_CECWAKESTATE:
243                 ret = copy_to_user(argp, &cec_dev->hdmi->sleep, sizeof(int));
244                 break;
245
246         default:
247                 break;
248         }
249         return 0;
250 }
251
252 static const struct file_operations cec_fops = {
253         .owner          = THIS_MODULE,
254         .compat_ioctl   = cec_ioctl,
255         .unlocked_ioctl = cec_ioctl,
256 };
257
258 int rockchip_hdmi_cec_init(struct hdmi *hdmi,
259                            int (*sendframe)(struct hdmi *,
260                                             struct cec_framedata *),
261                            int (*readframe)(struct hdmi *,
262                                             struct cec_framedata *),
263                            void (*setceclogicaddr)(struct hdmi *, int))
264 {
265         int ret, i;
266
267         cec_dev = kmalloc(sizeof(*cec_dev), GFP_KERNEL);
268         if (!cec_dev)
269                 return -ENOMEM;
270
271         memset(cec_dev, 0, sizeof(struct cec_device));
272         mutex_init(&cec_dev->cec_lock);
273         INIT_LIST_HEAD(&cec_dev->ceclist);
274         cec_dev->hdmi = hdmi;
275         cec_dev->enable = 1;
276         cec_dev->sendframe = sendframe;
277         cec_dev->readframe = readframe;
278         cec_dev->setceclogicaddr = setceclogicaddr;
279         cec_dev->workqueue = create_singlethread_workqueue("hdmi-cec");
280         if (!cec_dev->workqueue) {
281                 pr_err("HDMI CEC: create workqueue failed.\n");
282                 return -1;
283         }
284         cec_dev->device.minor = MISC_DYNAMIC_MINOR;
285         cec_dev->device.name = "cec";
286         cec_dev->device.mode = 0666;
287         cec_dev->device.fops = &cec_fops;
288         if (misc_register(&cec_dev->device)) {
289                 pr_err("CEC: Could not add cec misc driver\n");
290                 goto error;
291         }
292         for (i = 0; i < ARRAY_SIZE(cec_attrs); i++) {
293                 ret = device_create_file(cec_dev->device.this_device,
294                                          &cec_attrs[i]);
295                 if (ret) {
296                         pr_err("CEC: Could not add sys file\n");
297                         goto error1;
298                 }
299         }
300         return 0;
301
302 error1:
303         misc_deregister(&cec_dev->device);
304 error:
305         return -EINVAL;
306 }