This source file includes following definitions.
- to_versatile_panel
- versatile_panel_disable
- versatile_panel_enable
- versatile_panel_get_modes
- versatile_panel_probe
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include <linux/bitops.h>
30 #include <linux/init.h>
31 #include <linux/kernel.h>
32 #include <linux/mfd/syscon.h>
33 #include <linux/mod_devicetable.h>
34 #include <linux/module.h>
35 #include <linux/platform_device.h>
36 #include <linux/regmap.h>
37
38 #include <video/of_videomode.h>
39 #include <video/videomode.h>
40
41 #include <drm/drm_modes.h>
42 #include <drm/drm_panel.h>
43
44
45
46
47
48
49 #define SYS_CLCD 0x50
50
51
52 #define SYS_CLCD_CLCDID_MASK (BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12))
53 #define SYS_CLCD_ID_SANYO_3_8 (0x00 << 8)
54 #define SYS_CLCD_ID_SHARP_8_4 (0x01 << 8)
55 #define SYS_CLCD_ID_EPSON_2_2 (0x02 << 8)
56 #define SYS_CLCD_ID_SANYO_2_5 (0x07 << 8)
57 #define SYS_CLCD_ID_VGA (0x1f << 8)
58
59
60 #define IB2_CTRL 0x00
61 #define IB2_CTRL_LCD_SD BIT(1)
62 #define IB2_CTRL_LCD_BL_ON BIT(0)
63 #define IB2_CTRL_LCD_MASK (BIT(0)|BIT(1))
64
65
66
67
68 struct versatile_panel_type {
69
70
71
72 const char *name;
73
74
75
76 u32 magic;
77
78
79
80 struct drm_display_mode mode;
81
82
83
84 u32 bus_flags;
85
86
87
88 u32 width_mm;
89
90
91
92 u32 height_mm;
93
94
95
96 bool ib2;
97 };
98
99
100
101
102 struct versatile_panel {
103
104
105
106 struct device *dev;
107
108
109
110 struct drm_panel panel;
111
112
113
114 const struct versatile_panel_type *panel_type;
115
116
117
118 struct regmap *map;
119
120
121
122 struct regmap *ib2_map;
123 };
124
125 static const struct versatile_panel_type versatile_panels[] = {
126
127
128
129
130
131 {
132 .name = "Sanyo TM38QV67A02A",
133 .magic = SYS_CLCD_ID_SANYO_3_8,
134 .width_mm = 79,
135 .height_mm = 54,
136 .mode = {
137 .clock = 10000,
138 .hdisplay = 320,
139 .hsync_start = 320 + 6,
140 .hsync_end = 320 + 6 + 6,
141 .htotal = 320 + 6 + 6 + 6,
142 .vdisplay = 240,
143 .vsync_start = 240 + 5,
144 .vsync_end = 240 + 5 + 6,
145 .vtotal = 240 + 5 + 6 + 5,
146 .vrefresh = 116,
147 .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
148 },
149 },
150
151
152
153
154
155 {
156 .name = "Sharp LQ084V1DG21",
157 .magic = SYS_CLCD_ID_SHARP_8_4,
158 .width_mm = 171,
159 .height_mm = 130,
160 .mode = {
161 .clock = 25000,
162 .hdisplay = 640,
163 .hsync_start = 640 + 24,
164 .hsync_end = 640 + 24 + 96,
165 .htotal = 640 + 24 + 96 + 24,
166 .vdisplay = 480,
167 .vsync_start = 480 + 11,
168 .vsync_end = 480 + 11 + 2,
169 .vtotal = 480 + 11 + 2 + 32,
170 .vrefresh = 60,
171 .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
172 },
173 },
174
175
176
177
178 {
179 .name = "Epson L2F50113T00",
180 .magic = SYS_CLCD_ID_EPSON_2_2,
181 .width_mm = 34,
182 .height_mm = 45,
183 .mode = {
184 .clock = 62500,
185 .hdisplay = 176,
186 .hsync_start = 176 + 2,
187 .hsync_end = 176 + 2 + 3,
188 .htotal = 176 + 2 + 3 + 3,
189 .vdisplay = 220,
190 .vsync_start = 220 + 0,
191 .vsync_end = 220 + 0 + 2,
192 .vtotal = 220 + 0 + 2 + 1,
193 .vrefresh = 390,
194 .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
195 },
196 .bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
197 },
198
199
200
201
202 {
203 .name = "Sanyo ALR252RGT",
204 .magic = SYS_CLCD_ID_SANYO_2_5,
205 .width_mm = 37,
206 .height_mm = 50,
207 .mode = {
208 .clock = 5400,
209 .hdisplay = 240,
210 .hsync_start = 240 + 10,
211 .hsync_end = 240 + 10 + 10,
212 .htotal = 240 + 10 + 10 + 20,
213 .vdisplay = 320,
214 .vsync_start = 320 + 2,
215 .vsync_end = 320 + 2 + 2,
216 .vtotal = 320 + 2 + 2 + 2,
217 .vrefresh = 116,
218 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
219 },
220 .bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
221 .ib2 = true,
222 },
223 };
224
225 static inline struct versatile_panel *
226 to_versatile_panel(struct drm_panel *panel)
227 {
228 return container_of(panel, struct versatile_panel, panel);
229 }
230
231 static int versatile_panel_disable(struct drm_panel *panel)
232 {
233 struct versatile_panel *vpanel = to_versatile_panel(panel);
234
235
236 if (vpanel->ib2_map) {
237 dev_dbg(vpanel->dev, "disable IB2 display\n");
238 regmap_update_bits(vpanel->ib2_map,
239 IB2_CTRL,
240 IB2_CTRL_LCD_MASK,
241 IB2_CTRL_LCD_SD);
242 }
243
244 return 0;
245 }
246
247 static int versatile_panel_enable(struct drm_panel *panel)
248 {
249 struct versatile_panel *vpanel = to_versatile_panel(panel);
250
251
252 if (vpanel->ib2_map) {
253 dev_dbg(vpanel->dev, "enable IB2 display\n");
254 regmap_update_bits(vpanel->ib2_map,
255 IB2_CTRL,
256 IB2_CTRL_LCD_MASK,
257 IB2_CTRL_LCD_BL_ON);
258 }
259
260 return 0;
261 }
262
263 static int versatile_panel_get_modes(struct drm_panel *panel)
264 {
265 struct drm_connector *connector = panel->connector;
266 struct versatile_panel *vpanel = to_versatile_panel(panel);
267 struct drm_display_mode *mode;
268
269 connector->display_info.width_mm = vpanel->panel_type->width_mm;
270 connector->display_info.height_mm = vpanel->panel_type->height_mm;
271 connector->display_info.bus_flags = vpanel->panel_type->bus_flags;
272
273 mode = drm_mode_duplicate(panel->drm, &vpanel->panel_type->mode);
274 drm_mode_set_name(mode);
275 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
276
277 mode->width_mm = vpanel->panel_type->width_mm;
278 mode->height_mm = vpanel->panel_type->height_mm;
279 drm_mode_probed_add(connector, mode);
280
281 return 1;
282 }
283
284 static const struct drm_panel_funcs versatile_panel_drm_funcs = {
285 .disable = versatile_panel_disable,
286 .enable = versatile_panel_enable,
287 .get_modes = versatile_panel_get_modes,
288 };
289
290 static int versatile_panel_probe(struct platform_device *pdev)
291 {
292 struct device *dev = &pdev->dev;
293 struct versatile_panel *vpanel;
294 struct device *parent;
295 struct regmap *map;
296 int ret;
297 u32 val;
298 int i;
299
300 parent = dev->parent;
301 if (!parent) {
302 dev_err(dev, "no parent for versatile panel\n");
303 return -ENODEV;
304 }
305 map = syscon_node_to_regmap(parent->of_node);
306 if (IS_ERR(map)) {
307 dev_err(dev, "no regmap for versatile panel parent\n");
308 return PTR_ERR(map);
309 }
310
311 vpanel = devm_kzalloc(dev, sizeof(*vpanel), GFP_KERNEL);
312 if (!vpanel)
313 return -ENOMEM;
314
315 ret = regmap_read(map, SYS_CLCD, &val);
316 if (ret) {
317 dev_err(dev, "cannot access syscon regs\n");
318 return ret;
319 }
320
321 val &= SYS_CLCD_CLCDID_MASK;
322
323 for (i = 0; i < ARRAY_SIZE(versatile_panels); i++) {
324 const struct versatile_panel_type *pt;
325
326 pt = &versatile_panels[i];
327 if (pt->magic == val) {
328 vpanel->panel_type = pt;
329 break;
330 }
331 }
332
333
334 if (i == ARRAY_SIZE(versatile_panels)) {
335 dev_info(dev, "no panel detected\n");
336 return -ENODEV;
337 }
338
339 dev_info(dev, "detected: %s\n", vpanel->panel_type->name);
340 vpanel->dev = dev;
341 vpanel->map = map;
342
343
344 if (vpanel->panel_type->ib2) {
345 vpanel->ib2_map = syscon_regmap_lookup_by_compatible(
346 "arm,versatile-ib2-syscon");
347 if (IS_ERR(vpanel->ib2_map))
348 vpanel->ib2_map = NULL;
349 else
350 dev_info(dev, "panel mounted on IB2 daughterboard\n");
351 }
352
353 drm_panel_init(&vpanel->panel);
354 vpanel->panel.dev = dev;
355 vpanel->panel.funcs = &versatile_panel_drm_funcs;
356
357 return drm_panel_add(&vpanel->panel);
358 }
359
360 static const struct of_device_id versatile_panel_match[] = {
361 { .compatible = "arm,versatile-tft-panel", },
362 {},
363 };
364 MODULE_DEVICE_TABLE(of, versatile_panel_match);
365
366 static struct platform_driver versatile_panel_driver = {
367 .probe = versatile_panel_probe,
368 .driver = {
369 .name = "versatile-tft-panel",
370 .of_match_table = versatile_panel_match,
371 },
372 };
373 module_platform_driver(versatile_panel_driver);
374
375 MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
376 MODULE_DESCRIPTION("ARM Versatile panel driver");
377 MODULE_LICENSE("GPL v2");