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"
13 static struct cec_device *cec_dev;
15 static int cecreadframe(struct cec_framedata *frame)
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);
27 static int cecsendframe(struct cec_framedata *frame)
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);
39 static void cecsetlogicaddr(int addr)
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);
48 static void cecworkfunc(struct work_struct *work)
50 struct cec_delayed_work *cec_w =
51 container_of(work, struct cec_delayed_work, work.work);
52 struct cecframelist *list_node;
54 switch (cec_w->event) {
58 list_node = kmalloc(sizeof(*list_node), GFP_KERNEL);
61 cecreadframe(&list_node->cecframe);
62 if (cec_dev->enable) {
63 mutex_lock(&cec_dev->cec_lock);
64 list_add_tail(&list_node->framelist,
66 sysfs_notify(&cec_dev->device.this_device->kobj,
68 mutex_unlock(&cec_dev->cec_lock);
81 void rockchip_hdmi_cec_submit_work(int event, int delay, void *data)
83 struct cec_delayed_work *work;
85 HDMIDBG(1, "%s event %04x delay %d\n", __func__, event, delay);
90 work = kmalloc(sizeof(*work), GFP_ATOMIC);
93 INIT_DELAYED_WORK(&work->work, cecworkfunc);
96 queue_delayed_work(cec_dev->workqueue,
98 msecs_to_jiffies(delay));
100 HDMIDBG(1, "CEC: Cannot allocate memory\n");
104 void rockchip_hdmi_cec_set_pa(int devpa)
106 struct list_head *pos, *n;
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) {
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);
125 static ssize_t cec_enable_show(struct device *dev,
126 struct device_attribute *attr, char *buf)
128 return snprintf(buf, PAGE_SIZE, "%d\n", cec_dev->enable);
131 static ssize_t cec_enable_store(struct device *dev,
132 struct device_attribute *attr,
133 const char *buf, size_t count)
137 ret = kstrtoint(buf, 0, &cec_dev->enable);
141 static ssize_t cec_phy_show(struct device *dev,
142 struct device_attribute *attr, char *buf)
144 return snprintf(buf, PAGE_SIZE, "0x%x\n", cec_dev->address_phy);
147 static ssize_t cec_phy_store(struct device *dev,
148 struct device_attribute *attr,
149 const char *buf, size_t count)
153 ret = kstrtoint(buf, 0, &cec_dev->address_phy);
157 static ssize_t cec_logic_show(struct device *dev,
158 struct device_attribute *attr, char *buf)
160 return snprintf(buf, PAGE_SIZE, "0x%02x\n", cec_dev->address_logic);
163 static ssize_t cec_logic_store(struct device *dev,
164 struct device_attribute *attr,
165 const char *buf, size_t count)
169 ret = kstrtoint(buf, 0, &cec_dev->address_logic);
173 static ssize_t cec_state_show(struct device *dev,
174 struct device_attribute *attr, char *buf)
178 mutex_lock(&cec_dev->cec_lock);
179 if (!cec_dev->address_phy)
181 else if (list_empty(&cec_dev->ceclist))
185 mutex_unlock(&cec_dev->cec_lock);
186 return snprintf(buf, PAGE_SIZE, "%d\n", stat);
189 static struct device_attribute cec_attrs[] = {
190 __ATTR(logic, 0644, cec_logic_show, cec_logic_store),
191 __ATTR(phy, 0644, cec_phy_show, cec_phy_store),
192 __ATTR(enable, 0644, cec_enable_show, cec_enable_store),
193 __ATTR(stat, S_IRUGO, cec_state_show, NULL),
196 static long cec_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
200 struct cec_framedata cecsendtemp;
201 struct cecframelist *listemp;
203 argp = (void __user *)arg;
205 case HDMI_IOCTL_CECSETLA:
206 ret = copy_from_user(&cec_dev->address_logic,
208 cecsetlogicaddr(cec_dev->address_logic);
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));
218 case HDMI_IOCTL_CECENAB:
219 ret = copy_from_user(&cec_dev->enable, argp, sizeof(int));
221 case HDMI_IOCTL_CECPHY:
222 ret = copy_to_user(argp, &cec_dev->address_phy, sizeof(int));
224 case HDMI_IOCTL_CECLOGIC:
225 ret = copy_to_user(argp, &cec_dev->address_logic,
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);
238 mutex_unlock(&cec_dev->cec_lock);
240 case HDMI_IOCTL_CECCLEARLA:
242 case HDMI_IOCTL_CECWAKESTATE:
243 ret = copy_to_user(argp, &cec_dev->hdmi->sleep, sizeof(int));
252 static const struct file_operations cec_fops = {
253 .owner = THIS_MODULE,
254 .compat_ioctl = cec_ioctl,
255 .unlocked_ioctl = cec_ioctl,
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))
267 cec_dev = kmalloc(sizeof(*cec_dev), GFP_KERNEL);
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;
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");
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");
292 for (i = 0; i < ARRAY_SIZE(cec_attrs); i++) {
293 ret = device_create_file(cec_dev->device.this_device,
296 pr_err("CEC: Could not add sys file\n");
303 misc_deregister(&cec_dev->device);