Merge remote-tracking branch 'lsk/v3.10/topic/gator' into linux-linaro-lsk
[firefly-linux-kernel-4.4.55.git] / drivers / video / vexpress-dvi.c
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License version 2 as
4  * published by the Free Software Foundation.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9  * GNU General Public License for more details.
10  *
11  * Copyright (C) 2012 ARM Limited
12  */
13
14 #define pr_fmt(fmt) "vexpress-dvi: " fmt
15
16 #include <linux/fb.h>
17 #include <linux/of.h>
18 #include <linux/of_device.h>
19 #include <linux/vexpress.h>
20
21
22 static struct vexpress_config_func *vexpress_dvimode_func;
23
24 static struct {
25         u32 xres, yres, mode;
26 } vexpress_dvi_dvimodes[] = {
27         { 640, 480, 0 }, /* VGA */
28         { 800, 600, 1 }, /* SVGA */
29         { 1024, 768, 2 }, /* XGA */
30         { 1280, 1024, 3 }, /* SXGA */
31         { 1600, 1200, 4 }, /* UXGA */
32         { 1920, 1080, 5 }, /* HD1080 */
33 };
34
35 static void vexpress_dvi_mode_set(struct fb_info *info, u32 xres, u32 yres)
36 {
37         int err = -ENOENT;
38         int i;
39
40         if (!vexpress_dvimode_func)
41                 return;
42
43         for (i = 0; i < ARRAY_SIZE(vexpress_dvi_dvimodes); i++) {
44                 if (vexpress_dvi_dvimodes[i].xres == xres &&
45                                 vexpress_dvi_dvimodes[i].yres == yres) {
46                         pr_debug("mode: %ux%u = %d\n", xres, yres,
47                                         vexpress_dvi_dvimodes[i].mode);
48                         err = vexpress_config_write(vexpress_dvimode_func, 0,
49                                         vexpress_dvi_dvimodes[i].mode);
50                         break;
51                 }
52         }
53
54         if (err)
55                 pr_warn("Failed to set %ux%u mode! (%d)\n", xres, yres, err);
56 }
57
58
59 static struct vexpress_config_func *vexpress_muxfpga_func;
60 static int vexpress_dvi_fb = -1;
61
62 static int vexpress_dvi_mux_set(struct fb_info *info)
63 {
64         int err;
65         u32 site = vexpress_get_site_by_dev(info->device);
66
67         if (!vexpress_muxfpga_func)
68                 return -ENXIO;
69
70         err = vexpress_config_write(vexpress_muxfpga_func, 0, site);
71         if (!err) {
72                 pr_debug("Selected MUXFPGA input %d (fb%d)\n", site,
73                                 info->node);
74                 vexpress_dvi_fb = info->node;
75                 vexpress_dvi_mode_set(info, info->var.xres,
76                                 info->var.yres);
77         } else {
78                 pr_warn("Failed to select MUXFPGA input %d (fb%d)! (%d)\n",
79                                 site, info->node, err);
80         }
81
82         return err;
83 }
84
85 static int vexpress_dvi_fb_select(int fb)
86 {
87         int err;
88         struct fb_info *info;
89
90         /* fb0 is the default */
91         if (fb < 0)
92                 fb = 0;
93
94         info = registered_fb[fb];
95         if (!info || !lock_fb_info(info))
96                 return -ENODEV;
97
98         err = vexpress_dvi_mux_set(info);
99
100         unlock_fb_info(info);
101
102         return err;
103 }
104
105 static ssize_t vexpress_dvi_fb_show(struct device *dev,
106                 struct device_attribute *attr, char *buf)
107 {
108         return sprintf(buf, "%d\n", vexpress_dvi_fb);
109 }
110
111 static ssize_t vexpress_dvi_fb_store(struct device *dev,
112                 struct device_attribute *attr, const char *buf, size_t count)
113 {
114         long value;
115         int err = kstrtol(buf, 0, &value);
116
117         if (!err)
118                 err = vexpress_dvi_fb_select(value);
119
120         return err ? err : count;
121 }
122
123 DEVICE_ATTR(fb, S_IRUGO | S_IWUSR, vexpress_dvi_fb_show,
124                 vexpress_dvi_fb_store);
125
126
127 static int vexpress_dvi_fb_event_notify(struct notifier_block *self,
128                               unsigned long action, void *data)
129 {
130         struct fb_event *event = data;
131         struct fb_info *info = event->info;
132         struct fb_videomode *mode = event->data;
133
134         switch (action) {
135         case FB_EVENT_FB_REGISTERED:
136                 if (vexpress_dvi_fb < 0)
137                         vexpress_dvi_mux_set(info);
138                 break;
139         case FB_EVENT_MODE_CHANGE:
140         case FB_EVENT_MODE_CHANGE_ALL:
141                 if (info->node == vexpress_dvi_fb)
142                         vexpress_dvi_mode_set(info, mode->xres, mode->yres);
143                 break;
144         }
145
146         return NOTIFY_OK;
147 }
148
149 static struct notifier_block vexpress_dvi_fb_notifier = {
150         .notifier_call = vexpress_dvi_fb_event_notify,
151 };
152 static bool vexpress_dvi_fb_notifier_registered;
153
154
155 enum vexpress_dvi_func { FUNC_MUXFPGA, FUNC_DVIMODE };
156
157 static struct of_device_id vexpress_dvi_of_match[] = {
158         {
159                 .compatible = "arm,vexpress-muxfpga",
160                 .data = (void *)FUNC_MUXFPGA,
161         }, {
162                 .compatible = "arm,vexpress-dvimode",
163                 .data = (void *)FUNC_DVIMODE,
164         },
165         {}
166 };
167
168 static int vexpress_dvi_probe(struct platform_device *pdev)
169 {
170         enum vexpress_dvi_func func;
171         const struct of_device_id *match =
172                         of_match_device(vexpress_dvi_of_match, &pdev->dev);
173
174         if (match)
175                 func = (enum vexpress_dvi_func)match->data;
176         else
177                 func = pdev->id_entry->driver_data;
178
179         switch (func) {
180         case FUNC_MUXFPGA:
181                 vexpress_muxfpga_func =
182                                 vexpress_config_func_get_by_dev(&pdev->dev);
183                 device_create_file(&pdev->dev, &dev_attr_fb);
184                 break;
185         case FUNC_DVIMODE:
186                 vexpress_dvimode_func =
187                                 vexpress_config_func_get_by_dev(&pdev->dev);
188                 break;
189         }
190
191         if (!vexpress_dvi_fb_notifier_registered) {
192                 fb_register_client(&vexpress_dvi_fb_notifier);
193                 vexpress_dvi_fb_notifier_registered = true;
194         }
195
196         vexpress_dvi_fb_select(vexpress_dvi_fb);
197
198         return 0;
199 }
200
201 static const struct platform_device_id vexpress_dvi_id_table[] = {
202         { .name = "vexpress-muxfpga", .driver_data = FUNC_MUXFPGA, },
203         { .name = "vexpress-dvimode", .driver_data = FUNC_DVIMODE, },
204         {}
205 };
206
207 static struct platform_driver vexpress_dvi_driver = {
208         .probe = vexpress_dvi_probe,
209         .driver = {
210                 .name = "vexpress-dvi",
211                 .of_match_table = vexpress_dvi_of_match,
212         },
213         .id_table = vexpress_dvi_id_table,
214 };
215
216 static int __init vexpress_dvi_init(void)
217 {
218         return platform_driver_register(&vexpress_dvi_driver);
219 }
220 device_initcall(vexpress_dvi_init);