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/workqueue.h>
10 #include <linux/firmware.h>
11 #include "rockchip-hdmi-cec.h"
12 #include "linux/ioctl.h"
13 #include "linux/pagemap.h"
15 static struct cec_device *cec_dev;
17 static int cecreadframe(struct cec_framedata *frame)
19 if (frame == NULL || !cec_dev ||
20 cec_dev->readframe == NULL || !cec_dev->enable)
23 return cec_dev->readframe(cec_dev->hdmi, frame);
26 static int cecsendframe(struct cec_framedata *frame)
28 if (frame == NULL || !cec_dev || cec_dev->readframe == NULL)
31 return cec_dev->sendframe(cec_dev->hdmi, frame);
34 static void cecworkfunc(struct work_struct *work)
36 struct cec_delayed_work *cec_w =
37 container_of(work, struct cec_delayed_work, work.work);
38 struct cecframelist *list_node;
40 switch (cec_w->event) {
44 list_node = kmalloc(sizeof(*list_node), GFP_KERNEL);
45 if (list_node == NULL) {
46 pr_err("HDMI CEC: list kmalloc fail! ");
49 cecreadframe(&list_node->cecframe);
50 if (cec_dev->enable) {
51 mutex_lock(&cec_dev->cec_lock);
52 list_add_tail(&(list_node->framelist),
54 sysfs_notify(&cec_dev->device.this_device->kobj,
56 mutex_unlock(&cec_dev->cec_lock);
69 void rockchip_hdmi_cec_submit_work(int event, int delay, void *data)
71 struct cec_delayed_work *work;
73 CECDBG("%s event %04x delay %d\n", __func__, event, delay);
75 work = kmalloc(sizeof(*work), GFP_ATOMIC);
78 INIT_DELAYED_WORK(&work->work, cecworkfunc);
81 queue_delayed_work(cec_dev->workqueue,
83 msecs_to_jiffies(delay));
85 CECDBG(KERN_WARNING "CEC: Cannot allocate memory\n");
89 void rockchip_hdmi_cec_set_pa(int devpa)
91 struct list_head *pos, *n;
94 cec_dev->address_phy = devpa;
95 pr_info("%s %x\n", __func__, devpa);
96 /*when hdmi hpd , ceclist will be reset*/
97 mutex_lock(&cec_dev->cec_lock);
98 if (!list_empty(&cec_dev->ceclist)) {
99 list_for_each_safe(pos, n, &cec_dev->ceclist) {
104 INIT_LIST_HEAD(&cec_dev->ceclist);
105 sysfs_notify(&cec_dev->device.this_device->kobj, NULL, "stat");
106 mutex_unlock(&cec_dev->cec_lock);
110 static ssize_t cec_enable_show(struct device *dev,
111 struct device_attribute *attr, char *buf)
113 return snprintf(buf, PAGE_SIZE, "%d\n", cec_dev->enable);
116 static ssize_t cec_enable_store(struct device *dev,
117 struct device_attribute *attr,
118 const char *buf, size_t count)
122 ret = kstrtoint(buf, 0, &(cec_dev->enable));
126 static ssize_t cec_phy_show(struct device *dev,
127 struct device_attribute *attr, char *buf)
129 return snprintf(buf, PAGE_SIZE, "0x%x\n", cec_dev->address_phy);
132 static ssize_t cec_phy_store(struct device *dev,
133 struct device_attribute *attr,
134 const char *buf, size_t count)
138 ret = kstrtoint(buf, 0, &(cec_dev->address_phy));
142 static ssize_t cec_logic_show(struct device *dev,
143 struct device_attribute *attr, char *buf)
145 return snprintf(buf, PAGE_SIZE, "0x%02x\n", cec_dev->address_logic);
148 static ssize_t cec_logic_store(struct device *dev,
149 struct device_attribute *attr,
150 const char *buf, size_t count)
154 ret = kstrtoint(buf, 0, &(cec_dev->address_logic));
158 static ssize_t cec_state_show(struct device *dev,
159 struct device_attribute *attr, char *buf)
163 mutex_lock(&cec_dev->cec_lock);
164 if (!cec_dev->address_phy)
166 else if (list_empty(&cec_dev->ceclist))
170 mutex_unlock(&cec_dev->cec_lock);
171 return snprintf(buf, PAGE_SIZE, "%d\n", stat);
174 static struct device_attribute cec_attrs[] = {
175 __ATTR(logic, 0666, cec_logic_show, cec_logic_store),
176 __ATTR(phy, 0666, cec_phy_show, cec_phy_store),
177 __ATTR(enable, 0666, cec_enable_show, cec_enable_store),
178 __ATTR(stat, S_IRUGO, cec_state_show, NULL),
181 static long cec_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
185 struct cec_framedata cecsendtemp;
186 struct cecframelist *listemp;
188 argp = (void __user *)arg;
190 case HDMI_IOCTL_CECSETLA:
191 ret = copy_from_user(&cec_dev->address_logic,
193 if (cec_dev->setceclogicaddr)
194 cec_dev->setceclogicaddr(cec_dev->hdmi,
195 cec_dev->address_logic);
197 case HDMI_IOCTL_CECSEND:
198 ret = copy_from_user(&cecsendtemp, argp,
199 sizeof(struct cec_framedata));
200 ret = cecsendframe(&cecsendtemp);
201 cecsendtemp.returnval = ret;
202 ret = copy_to_user(argp, &cecsendtemp,
203 sizeof(struct cec_framedata));
205 case HDMI_IOCTL_CECENAB:
206 ret = copy_from_user(&cec_dev->enable, argp, sizeof(int));
208 case HDMI_IOCTL_CECPHY:
209 ret = copy_to_user(argp, &(cec_dev->address_phy), sizeof(int));
211 case HDMI_IOCTL_CECLOGIC:
212 ret = copy_to_user(argp, &(cec_dev->address_logic),
215 case HDMI_IOCTL_CECREAD:
216 mutex_lock(&cec_dev->cec_lock);
217 if (!list_empty(&cec_dev->ceclist)) {
218 listemp = list_entry(cec_dev->ceclist.next,
219 struct cecframelist, framelist);
220 ret = copy_to_user(argp, &listemp->cecframe,
221 sizeof(struct cec_framedata));
222 list_del(&listemp->framelist);
225 mutex_unlock(&cec_dev->cec_lock);
227 case HDMI_IOCTL_CECCLEARLA:
229 case HDMI_IOCTL_CECWAKESTATE:
230 ret = copy_to_user(argp, &(cec_dev->hdmi->sleep), sizeof(int));
239 static const struct file_operations cec_fops = {
240 .owner = THIS_MODULE,
241 .compat_ioctl = cec_ioctl,
242 .unlocked_ioctl = cec_ioctl,
245 int rockchip_hdmi_cec_init(struct hdmi *hdmi,
246 int (*sendframe)(struct hdmi *,
247 struct cec_framedata *),
248 int (*readframe)(struct hdmi *,
249 struct cec_framedata *),
250 void (*setceclogicaddr)(struct hdmi *, int))
254 cec_dev = kmalloc(sizeof(*cec_dev), GFP_KERNEL);
256 pr_err("HDMI CEC: kmalloc fail!");
259 memset(cec_dev, 0, sizeof(struct cec_device));
260 mutex_init(&cec_dev->cec_lock);
261 INIT_LIST_HEAD(&cec_dev->ceclist);
262 cec_dev->hdmi = hdmi;
264 cec_dev->sendframe = sendframe;
265 cec_dev->readframe = readframe;
266 cec_dev->setceclogicaddr = setceclogicaddr;
267 cec_dev->workqueue = create_singlethread_workqueue("hdmi-cec");
268 if (cec_dev->workqueue == NULL) {
269 pr_err("HDMI CEC: create workqueue failed.\n");
272 cec_dev->device.minor = MISC_DYNAMIC_MINOR;
273 cec_dev->device.name = "cec";
274 cec_dev->device.mode = 0666;
275 cec_dev->device.fops = &cec_fops;
276 if (misc_register(&cec_dev->device)) {
277 pr_err("CEC: Could not add cec misc driver\n");
280 for (i = 0; i < ARRAY_SIZE(cec_attrs); i++) {
281 ret = device_create_file(cec_dev->device.this_device,
284 pr_err("CEC: Could not add sys file\n");
291 misc_deregister(&cec_dev->device);