Merge branch 'develop-3.0' of ssh://10.10.10.29/rk/kernel into develop-3.0
[firefly-linux-kernel-4.4.55.git] / drivers / video / hdmi / hdmi-core.c
1 #include <linux/kernel.h>\r
2 #include <linux/delay.h>\r
3 #include <linux/module.h>\r
4 #include <linux/err.h>\r
5 #include <linux/slab.h>\r
6 #include <linux/hdmi.h>\r
7 #include <linux/input.h>\r
8 \r
9 \r
10 struct class *hdmi_class;\r
11 struct hdmi_id_ref_info {\r
12         struct hdmi *hdmi;\r
13         int id;\r
14         int ref;\r
15 }ref_info[HDMI_MAX_ID];\r
16 #ifdef CONFIG_SYSFS\r
17 \r
18 extern int hdmi_create_attrs(struct hdmi *hdmi);\r
19 extern void hdmi_remove_attrs(struct hdmi *hdmi);\r
20 \r
21 #else\r
22 \r
23 static inline int hdmi_create_attrs(struct hdmi *hdmi)\r
24 { return 0; }\r
25 static inline void hdmi_remove_attrs(struct hdmi *hdmi) {}\r
26 \r
27 #endif /* CONFIG_SYSFS */\r
28 static void __hdmi_changed(struct hdmi *hdmi)\r
29 {\r
30         int precent;\r
31         \r
32         mutex_lock(&hdmi->lock);\r
33         precent = hdmi->ops->hdmi_precent(hdmi);\r
34         if(precent && (hdmi->mode == DISP_ON_LCD) && hdmi->display_on){\r
35                 if(hdmi->ops->insert(hdmi) == 0){\r
36                         hdmi->mode = hdmi->display_on;\r
37                         kobject_uevent(&hdmi->dev->kobj, KOBJ_CHANGE);\r
38                 }\r
39                 else\r
40                         hdmi_dbg(hdmi->dev, "insert error\n");\r
41         hdmi_set_backlight(hdmi->display_on==DISP_ON_HDMI?HDMI_DISABLE: HDMI_ENABLE);\r
42 \r
43         }\r
44         else if(precent &&(hdmi->mode != hdmi->display_on)&& hdmi->display_on){\r
45             hdmi->mode = hdmi->display_on;\r
46         hdmi_set_backlight(hdmi->display_on==DISP_ON_HDMI?HDMI_DISABLE: HDMI_ENABLE); \r
47         }\r
48         else if((!precent || !hdmi->display_on) && hdmi->mode != DISP_ON_LCD){\r
49                 if(hdmi->ops->remove(hdmi) == 0){\r
50                         hdmi->mode = DISP_ON_LCD;\r
51                         hdmi_set_backlight(HDMI_ENABLE);\r
52                         kobject_uevent(&hdmi->dev->kobj, KOBJ_CHANGE);\r
53                 }\r
54                 else\r
55                         hdmi_dbg(hdmi->dev, "remove error\n");\r
56         }\r
57         mutex_unlock(&hdmi->lock);\r
58         return;\r
59 }\r
60 \r
61 void hdmi_changed(struct hdmi *hdmi, int msec)\r
62 {       \r
63         schedule_delayed_work(&hdmi->work, msecs_to_jiffies(msec));\r
64         return;\r
65 }\r
66 void hdmi_suspend(struct hdmi *hdmi)\r
67 {\r
68         del_timer(&hdmi->timer);\r
69         flush_delayed_work(&hdmi->work);\r
70         if(hdmi->mode != DISP_ON_LCD){\r
71                 hdmi->ops->remove(hdmi);\r
72                 hdmi->mode = DISP_ON_LCD;\r
73         }\r
74         return;\r
75 }\r
76 void hdmi_resume(struct hdmi *hdmi)\r
77 {\r
78         mod_timer(&hdmi->timer, jiffies + msecs_to_jiffies(10));\r
79         return;\r
80 }\r
81 \r
82 static void hdmi_changed_work(struct work_struct *work)\r
83 {\r
84         struct hdmi *hdmi = container_of(work, struct hdmi,\r
85                                                 work.work);\r
86         \r
87         __hdmi_changed(hdmi);\r
88         return;\r
89 }\r
90 \r
91 void *hdmi_priv(struct hdmi *hdmi)\r
92 {\r
93         return (void *)hdmi->priv;\r
94 }\r
95 static void hdmi_detect_timer(unsigned long data)\r
96 {\r
97         struct hdmi *hdmi = (struct hdmi*)data;\r
98         \r
99         int precent =  hdmi->ops->hdmi_precent(hdmi);\r
100 \r
101         if((precent && hdmi->mode == DISP_ON_LCD) ||\r
102                         (!precent && hdmi->mode != DISP_ON_LCD))\r
103                 hdmi_changed(hdmi, 100);\r
104         mod_timer(&hdmi->timer, jiffies + msecs_to_jiffies(200));\r
105 }\r
106 struct hdmi *hdmi_register(int extra, struct device *parent)\r
107 {\r
108         int rc = 0, i;\r
109         char name[8];\r
110         struct hdmi *hdmi = kzalloc(sizeof(struct hdmi)+ extra, GFP_KERNEL);\r
111 \r
112         if(!hdmi)\r
113                 return NULL;\r
114         for(i = 0; i < HDMI_MAX_ID; i++) \r
115         {\r
116                 if(ref_info[i].ref == 0)\r
117                 {\r
118                         ref_info[i].ref = 1;\r
119                         hdmi->id = i;\r
120                         break;\r
121                 }\r
122         }\r
123         if(i == HDMI_MAX_ID)\r
124         {\r
125                 kfree(hdmi);\r
126                 return NULL;\r
127         }\r
128         sprintf(name, "hdmi-%d", hdmi->id);\r
129         \r
130         hdmi->dev = device_create(hdmi_class, parent, 0,\r
131                                  "%s", name);\r
132         if (IS_ERR(hdmi->dev)) {\r
133                 rc = PTR_ERR(hdmi->dev);\r
134                 goto dev_create_failed;\r
135         }\r
136 \r
137         dev_set_drvdata(hdmi->dev, hdmi);\r
138         ref_info[i].hdmi = hdmi;\r
139 \r
140         INIT_DELAYED_WORK(&hdmi->work, hdmi_changed_work);\r
141 \r
142         rc = hdmi_create_attrs(hdmi);\r
143         if (rc)\r
144                 goto create_attrs_failed;\r
145 \r
146         goto success;\r
147 \r
148 create_attrs_failed:\r
149         device_unregister(hdmi->dev);\r
150 dev_create_failed:\r
151         hdmi_remove_attrs(hdmi);\r
152         kfree(hdmi);\r
153         return NULL;\r
154 success:\r
155         mutex_init(&hdmi->lock);\r
156         setup_timer(&hdmi->timer, hdmi_detect_timer,(unsigned long)hdmi);\r
157         mod_timer(&hdmi->timer, jiffies + msecs_to_jiffies(200));\r
158         return hdmi;\r
159 }\r
160 void hdmi_unregister(struct hdmi *hdmi)\r
161 {\r
162         int id;\r
163 \r
164         if(!hdmi)\r
165                 return;\r
166         id = hdmi->id;\r
167         del_timer(&hdmi->timer);\r
168         flush_scheduled_work();\r
169         hdmi_remove_attrs(hdmi);\r
170         device_unregister(hdmi->dev);\r
171 \r
172         kfree(hdmi);\r
173         hdmi = NULL;\r
174         ref_info[id].ref = 0;\r
175         ref_info[id].hdmi = NULL;\r
176 }\r
177 struct hdmi *get_hdmi_struct(int nr)\r
178 {\r
179         if(ref_info[nr].ref == 0)\r
180                 return NULL;\r
181         else\r
182                 return ref_info[nr].hdmi;\r
183 }\r
184 int hdmi_is_insert(void)\r
185 {\r
186         struct hdmi *hdmi = get_hdmi_struct(0);\r
187 \r
188         if(hdmi && hdmi->ops && hdmi->ops->hdmi_precent)\r
189                 return hdmi->ops->hdmi_precent(hdmi);\r
190         else\r
191                 return 0;\r
192 }\r
193 int hdmi_get_scale(void)\r
194 {\r
195         struct hdmi* hdmi = get_hdmi_struct(0);\r
196         if(!hdmi)\r
197                 return 100;\r
198         else if(hdmi->mode != DISP_ON_LCD)\r
199                 return hdmi->scale;\r
200         else\r
201             return 100;\r
202 }\r
203 \r
204 int hdmi_set_scale(int event, char *data, int len)\r
205 {\r
206         int result;\r
207         struct hdmi* hdmi = get_hdmi_struct(0);\r
208 \r
209         if(!hdmi)\r
210                 return -1;\r
211         if(len != 4)\r
212                 return -1;\r
213         if(fb_get_video_mode() || hdmi->mode == DISP_ON_LCD)\r
214                 return -1;\r
215 \r
216         result = data[0] | data[1]<<1 | data[2]<<2;\r
217         if(event != MOUSE_NONE && (result & event) != event)\r
218                 return -1;\r
219 \r
220         hdmi->scale += data[3];\r
221         \r
222         hdmi->scale = (hdmi->scale>100)?100:hdmi->scale;\r
223         hdmi->scale = (hdmi->scale<MIN_SCALE)?MIN_SCALE:hdmi->scale;\r
224         return 0;       \r
225 }\r
226 \r
227 static int __init hdmi_class_init(void)\r
228 {\r
229         int i;\r
230         \r
231         hdmi_class = class_create(THIS_MODULE, "hdmi");\r
232 \r
233         if (IS_ERR(hdmi_class))\r
234                 return PTR_ERR(hdmi_class);\r
235         for(i = 0; i < HDMI_MAX_ID; i++) {\r
236                 ref_info[i].id = i;\r
237                 ref_info[i].ref = 0;\r
238                 ref_info[i].hdmi = NULL;\r
239         }\r
240         return 0;\r
241 }\r
242 \r
243 static void __exit hdmi_class_exit(void)\r
244 {\r
245         class_destroy(hdmi_class);\r
246 }\r
247 EXPORT_SYMBOL(hdmi_changed);\r
248 EXPORT_SYMBOL(hdmi_register);\r
249 EXPORT_SYMBOL(hdmi_unregister);\r
250 EXPORT_SYMBOL(get_hdmi_struct);\r
251 \r
252 subsys_initcall(hdmi_class_init);\r
253 module_exit(hdmi_class_exit);\r
254 \r