aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Gaignard <benjamin.gaignard@linaro.org>2014-07-30 13:26:17 -0400
committerBenjamin Gaignard <benjamin.gaignard@linaro.org>2014-07-30 13:26:17 -0400
commitcdfbff788ec9d488cab6450a439c2421ef28b0a4 (patch)
tree83d8398f4f6f0a39843333582afc4fa39928b69a
parentc86a5f6e1b9c84064231ec867aca281d9e8514a2 (diff)
drm: sti: add TVOut driver
TVout hardware block is responsible to dispatch the data flow coming from compositor block to any of the output (HDMI or Analog TV). It control when output are start/stop and configure according the require flow path. TVout is the parent of HDMI and HDA drivers and bind them at runtime. Tvout is mapped on drm_encoder structure. One encoder is created for each of the sub-devices and link to their connector/bridge Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org> Reviewed-by: Rob Clark <robdclark@gmail.com>
-rw-r--r--drivers/gpu/drm/sti/Makefile3
-rw-r--r--drivers/gpu/drm/sti/sti_tvout.c648
2 files changed, 650 insertions, 1 deletions
diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile
index 4cf0c5561037..5f6d96d27f3b 100644
--- a/drivers/gpu/drm/sti/Makefile
+++ b/drivers/gpu/drm/sti/Makefile
@@ -6,4 +6,5 @@ obj-$(CONFIG_DRM_STI) = \
6 sti_vtg.o \ 6 sti_vtg.o \
7 sti_vtac.o \ 7 sti_vtac.o \
8 stihdmi.o \ 8 stihdmi.o \
9 sti_hda.o \ No newline at end of file 9 sti_hda.o \
10 sti_tvout.o \ No newline at end of file
diff --git a/drivers/gpu/drm/sti/sti_tvout.c b/drivers/gpu/drm/sti/sti_tvout.c
new file mode 100644
index 000000000000..b69e26fee76e
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_tvout.c
@@ -0,0 +1,648 @@
1/*
2 * Copyright (C) STMicroelectronics SA 2014
3 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
4 * Vincent Abriou <vincent.abriou@st.com>
5 * for STMicroelectronics.
6 * License terms: GNU General Public License (GPL), version 2
7 */
8
9#include <linux/clk.h>
10#include <linux/component.h>
11#include <linux/module.h>
12#include <linux/of_platform.h>
13#include <linux/platform_device.h>
14#include <linux/reset.h>
15
16#include <drm/drmP.h>
17#include <drm/drm_crtc_helper.h>
18
19/* glue registers */
20#define TVO_CSC_MAIN_M0 0x000
21#define TVO_CSC_MAIN_M1 0x004
22#define TVO_CSC_MAIN_M2 0x008
23#define TVO_CSC_MAIN_M3 0x00c
24#define TVO_CSC_MAIN_M4 0x010
25#define TVO_CSC_MAIN_M5 0x014
26#define TVO_CSC_MAIN_M6 0x018
27#define TVO_CSC_MAIN_M7 0x01c
28#define TVO_MAIN_IN_VID_FORMAT 0x030
29#define TVO_CSC_AUX_M0 0x100
30#define TVO_CSC_AUX_M1 0x104
31#define TVO_CSC_AUX_M2 0x108
32#define TVO_CSC_AUX_M3 0x10c
33#define TVO_CSC_AUX_M4 0x110
34#define TVO_CSC_AUX_M5 0x114
35#define TVO_CSC_AUX_M6 0x118
36#define TVO_CSC_AUX_M7 0x11c
37#define TVO_AUX_IN_VID_FORMAT 0x130
38#define TVO_VIP_HDF 0x400
39#define TVO_HD_SYNC_SEL 0x418
40#define TVO_HD_DAC_CFG_OFF 0x420
41#define TVO_VIP_HDMI 0x500
42#define TVO_HDMI_FORCE_COLOR_0 0x504
43#define TVO_HDMI_FORCE_COLOR_1 0x508
44#define TVO_HDMI_CLIP_VALUE_B_CB 0x50c
45#define TVO_HDMI_CLIP_VALUE_Y_G 0x510
46#define TVO_HDMI_CLIP_VALUE_R_CR 0x514
47#define TVO_HDMI_SYNC_SEL 0x518
48#define TVO_HDMI_DFV_OBS 0x540
49
50#define TVO_IN_FMT_SIGNED BIT(0)
51#define TVO_SYNC_EXT BIT(4)
52
53#define TVO_VIP_REORDER_R_SHIFT 24
54#define TVO_VIP_REORDER_G_SHIFT 20
55#define TVO_VIP_REORDER_B_SHIFT 16
56#define TVO_VIP_REORDER_MASK 0x3
57#define TVO_VIP_REORDER_Y_G_SEL 0
58#define TVO_VIP_REORDER_CB_B_SEL 1
59#define TVO_VIP_REORDER_CR_R_SEL 2
60
61#define TVO_VIP_CLIP_SHIFT 8
62#define TVO_VIP_CLIP_MASK 0x7
63#define TVO_VIP_CLIP_DISABLED 0
64#define TVO_VIP_CLIP_EAV_SAV 1
65#define TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y 2
66#define TVO_VIP_CLIP_LIMITED_RANGE_CB_CR 3
67#define TVO_VIP_CLIP_PROG_RANGE 4
68
69#define TVO_VIP_RND_SHIFT 4
70#define TVO_VIP_RND_MASK 0x3
71#define TVO_VIP_RND_8BIT_ROUNDED 0
72#define TVO_VIP_RND_10BIT_ROUNDED 1
73#define TVO_VIP_RND_12BIT_ROUNDED 2
74
75#define TVO_VIP_SEL_INPUT_MASK 0xf
76#define TVO_VIP_SEL_INPUT_MAIN 0x0
77#define TVO_VIP_SEL_INPUT_AUX 0x8
78#define TVO_VIP_SEL_INPUT_FORCE_COLOR 0xf
79#define TVO_VIP_SEL_INPUT_BYPASS_MASK 0x1
80#define TVO_VIP_SEL_INPUT_BYPASSED 1
81
82#define TVO_SYNC_MAIN_VTG_SET_REF 0x00
83#define TVO_SYNC_MAIN_VTG_SET_1 0x01
84#define TVO_SYNC_MAIN_VTG_SET_2 0x02
85#define TVO_SYNC_MAIN_VTG_SET_3 0x03
86#define TVO_SYNC_MAIN_VTG_SET_4 0x04
87#define TVO_SYNC_MAIN_VTG_SET_5 0x05
88#define TVO_SYNC_MAIN_VTG_SET_6 0x06
89#define TVO_SYNC_AUX_VTG_SET_REF 0x10
90#define TVO_SYNC_AUX_VTG_SET_1 0x11
91#define TVO_SYNC_AUX_VTG_SET_2 0x12
92#define TVO_SYNC_AUX_VTG_SET_3 0x13
93#define TVO_SYNC_AUX_VTG_SET_4 0x14
94#define TVO_SYNC_AUX_VTG_SET_5 0x15
95#define TVO_SYNC_AUX_VTG_SET_6 0x16
96
97#define TVO_SYNC_HD_DCS_SHIFT 8
98
99#define ENCODER_MAIN_CRTC_MASK BIT(0)
100
101/* enum listing the supported output data format */
102enum sti_tvout_video_out_type {
103 STI_TVOUT_VIDEO_OUT_RGB,
104 STI_TVOUT_VIDEO_OUT_YUV,
105};
106
107struct sti_tvout {
108 struct device *dev;
109 struct drm_device *drm_dev;
110 void __iomem *regs;
111 struct reset_control *reset;
112 struct drm_encoder *hdmi;
113 struct drm_encoder *hda;
114};
115
116struct sti_tvout_encoder {
117 struct drm_encoder encoder;
118 struct sti_tvout *tvout;
119};
120
121#define to_sti_tvout_encoder(x) \
122 container_of(x, struct sti_tvout_encoder, encoder)
123
124#define to_sti_tvout(x) to_sti_tvout_encoder(x)->tvout
125
126/* preformatter conversion matrix */
127static const u32 rgb_to_ycbcr_601[8] = {
128 0xF927082E, 0x04C9FEAB, 0x01D30964, 0xFA95FD3D,
129 0x0000082E, 0x00002000, 0x00002000, 0x00000000
130};
131
132/* 709 RGB to YCbCr */
133static const u32 rgb_to_ycbcr_709[8] = {
134 0xF891082F, 0x0367FF40, 0x01280B71, 0xF9B1FE20,
135 0x0000082F, 0x00002000, 0x00002000, 0x00000000
136};
137
138static u32 tvout_read(struct sti_tvout *tvout, int offset)
139{
140 return readl(tvout->regs + offset);
141}
142
143static void tvout_write(struct sti_tvout *tvout, u32 val, int offset)
144{
145 writel(val, tvout->regs + offset);
146}
147
148/**
149 * Set the clipping mode of a VIP
150 *
151 * @tvout: tvout structure
152 * @cr_r:
153 * @y_g:
154 * @cb_b:
155 */
156static void tvout_vip_set_color_order(struct sti_tvout *tvout,
157 u32 cr_r, u32 y_g, u32 cb_b)
158{
159 u32 val = tvout_read(tvout, TVO_VIP_HDMI);
160
161 val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT);
162 val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT);
163 val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_B_SHIFT);
164 val |= cr_r << TVO_VIP_REORDER_R_SHIFT;
165 val |= y_g << TVO_VIP_REORDER_G_SHIFT;
166 val |= cb_b << TVO_VIP_REORDER_B_SHIFT;
167
168 tvout_write(tvout, val, TVO_VIP_HDMI);
169}
170
171/**
172 * Set the clipping mode of a VIP
173 *
174 * @tvout: tvout structure
175 * @range: clipping range
176 */
177static void tvout_vip_set_clip_mode(struct sti_tvout *tvout, u32 range)
178{
179 u32 val = tvout_read(tvout, TVO_VIP_HDMI);
180
181 val &= ~(TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT);
182 val |= range << TVO_VIP_CLIP_SHIFT;
183 tvout_write(tvout, val, TVO_VIP_HDMI);
184}
185
186/**
187 * Set the rounded value of a VIP
188 *
189 * @tvout: tvout structure
190 * @rnd: rounded val per component
191 */
192static void tvout_vip_set_rnd(struct sti_tvout *tvout, u32 rnd)
193{
194 u32 val = tvout_read(tvout, TVO_VIP_HDMI);
195
196 val &= ~(TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT);
197 val |= rnd << TVO_VIP_RND_SHIFT;
198 tvout_write(tvout, val, TVO_VIP_HDMI);
199}
200
201/**
202 * Select the VIP input
203 *
204 * @tvout: tvout structure
205 * @sel_input: selected_input (main/aux + conv)
206 */
207static void tvout_vip_set_sel_input(struct sti_tvout *tvout,
208 bool main_path,
209 bool sel_input_logic_inverted,
210 enum sti_tvout_video_out_type video_out)
211{
212 u32 sel_input;
213 u32 val = tvout_read(tvout, TVO_VIP_HDMI);
214
215 if (main_path)
216 sel_input = TVO_VIP_SEL_INPUT_MAIN;
217 else
218 sel_input = TVO_VIP_SEL_INPUT_AUX;
219
220 switch (video_out) {
221 case STI_TVOUT_VIDEO_OUT_RGB:
222 sel_input |= TVO_VIP_SEL_INPUT_BYPASSED;
223 break;
224 case STI_TVOUT_VIDEO_OUT_YUV:
225 sel_input &= ~TVO_VIP_SEL_INPUT_BYPASSED;
226 break;
227 }
228
229 /* on stih407 chip the sel_input bypass mode logic is inverted */
230 if (sel_input_logic_inverted)
231 sel_input = sel_input ^ TVO_VIP_SEL_INPUT_BYPASS_MASK;
232
233 val &= ~TVO_VIP_SEL_INPUT_MASK;
234 val |= sel_input;
235 tvout_write(tvout, val, TVO_VIP_HDMI);
236}
237
238/**
239 * Select the input video signed or unsigned
240 *
241 * @tvout: tvout structure
242 * @in_vid_signed: used video input format
243 */
244static void tvout_vip_set_in_vid_fmt(struct sti_tvout *tvout, u32 in_vid_fmt)
245{
246 u32 val = tvout_read(tvout, TVO_VIP_HDMI);
247
248 val &= ~TVO_IN_FMT_SIGNED;
249 val |= in_vid_fmt;
250 tvout_write(tvout, val, TVO_MAIN_IN_VID_FORMAT);
251}
252
253/**
254 * Start VIP block for HDMI output
255 *
256 * @tvout: pointer on tvout structure
257 * @main_path: true if main path has to be used in the vip configuration
258 * else aux path is used.
259 */
260static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path)
261{
262 struct device_node *node = tvout->dev->of_node;
263 bool sel_input_logic_inverted = false;
264
265 dev_dbg(tvout->dev, "%s\n", __func__);
266
267 if (main_path) {
268 DRM_DEBUG_DRIVER("main vip for hdmi\n");
269 /* select the input sync for hdmi = VTG set 1 */
270 tvout_write(tvout, TVO_SYNC_MAIN_VTG_SET_1, TVO_HDMI_SYNC_SEL);
271 } else {
272 DRM_DEBUG_DRIVER("aux vip for hdmi\n");
273 /* select the input sync for hdmi = VTG set 1 */
274 tvout_write(tvout, TVO_SYNC_AUX_VTG_SET_1, TVO_HDMI_SYNC_SEL);
275 }
276
277 /* set color channel order */
278 tvout_vip_set_color_order(tvout,
279 TVO_VIP_REORDER_CR_R_SEL,
280 TVO_VIP_REORDER_Y_G_SEL,
281 TVO_VIP_REORDER_CB_B_SEL);
282
283 /* set clipping mode (Limited range RGB/Y) */
284 tvout_vip_set_clip_mode(tvout, TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y);
285
286 /* set round mode (rounded to 8-bit per component) */
287 tvout_vip_set_rnd(tvout, TVO_VIP_RND_8BIT_ROUNDED);
288
289 if (of_device_is_compatible(node, "st,stih407-tvout")) {
290 /* set input video format */
291 tvout_vip_set_in_vid_fmt(tvout->regs + TVO_MAIN_IN_VID_FORMAT,
292 TVO_IN_FMT_SIGNED);
293 sel_input_logic_inverted = true;
294 }
295
296 /* input selection */
297 tvout_vip_set_sel_input(tvout, main_path,
298 sel_input_logic_inverted, STI_TVOUT_VIDEO_OUT_RGB);
299}
300
301/**
302 * Start HDF VIP and HD DAC
303 *
304 * @tvout: pointer on tvout structure
305 * @main_path: true if main path has to be used in the vip configuration
306 * else aux path is used.
307 */
308static void tvout_hda_start(struct sti_tvout *tvout, bool main_path)
309{
310 struct device_node *node = tvout->dev->of_node;
311 bool sel_input_logic_inverted = false;
312
313 dev_dbg(tvout->dev, "%s\n", __func__);
314
315 if (!main_path) {
316 DRM_ERROR("HD Analog on aux not implemented\n");
317 return;
318 }
319
320 DRM_DEBUG_DRIVER("main vip for HDF\n");
321
322 /* set color channel order */
323 tvout_vip_set_color_order(tvout->regs + TVO_VIP_HDF,
324 TVO_VIP_REORDER_CR_R_SEL,
325 TVO_VIP_REORDER_Y_G_SEL,
326 TVO_VIP_REORDER_CB_B_SEL);
327
328 /* set clipping mode (Limited range RGB/Y) */
329 tvout_vip_set_clip_mode(tvout->regs + TVO_VIP_HDF,
330 TVO_VIP_CLIP_LIMITED_RANGE_CB_CR);
331
332 /* set round mode (rounded to 10-bit per component) */
333 tvout_vip_set_rnd(tvout->regs + TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED);
334
335 if (of_device_is_compatible(node, "st,stih407-tvout")) {
336 /* set input video format */
337 tvout_vip_set_in_vid_fmt(tvout, TVO_IN_FMT_SIGNED);
338 sel_input_logic_inverted = true;
339 }
340
341 /* Input selection */
342 tvout_vip_set_sel_input(tvout->regs + TVO_VIP_HDF,
343 main_path,
344 sel_input_logic_inverted,
345 STI_TVOUT_VIDEO_OUT_YUV);
346
347 /* select the input sync for HD analog = VTG set 3
348 * and HD DCS = VTG set 2 */
349 tvout_write(tvout,
350 (TVO_SYNC_MAIN_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT)
351 | TVO_SYNC_MAIN_VTG_SET_3,
352 TVO_HD_SYNC_SEL);
353
354 /* power up HD DAC */
355 tvout_write(tvout, 0, TVO_HD_DAC_CFG_OFF);
356}
357
358static void sti_tvout_encoder_dpms(struct drm_encoder *encoder, int mode)
359{
360}
361
362static bool sti_tvout_encoder_mode_fixup(struct drm_encoder *encoder,
363 const struct drm_display_mode *mode,
364 struct drm_display_mode *adjusted_mode)
365{
366 return true;
367}
368
369static void sti_tvout_encoder_mode_set(struct drm_encoder *encoder,
370 struct drm_display_mode *mode,
371 struct drm_display_mode *adjusted_mode)
372{
373}
374
375static void sti_tvout_encoder_prepare(struct drm_encoder *encoder)
376{
377}
378
379static void sti_tvout_encoder_destroy(struct drm_encoder *encoder)
380{
381 struct sti_tvout_encoder *sti_encoder = to_sti_tvout_encoder(encoder);
382
383 drm_encoder_cleanup(encoder);
384 kfree(sti_encoder);
385}
386
387static const struct drm_encoder_funcs sti_tvout_encoder_funcs = {
388 .destroy = sti_tvout_encoder_destroy,
389};
390
391static void sti_hda_encoder_commit(struct drm_encoder *encoder)
392{
393 struct sti_tvout *tvout = to_sti_tvout(encoder);
394
395 tvout_hda_start(tvout, true);
396}
397
398static void sti_hda_encoder_disable(struct drm_encoder *encoder)
399{
400 struct sti_tvout *tvout = to_sti_tvout(encoder);
401
402 /* reset VIP register */
403 tvout_write(tvout, 0x0, TVO_VIP_HDF);
404
405 /* power down HD DAC */
406 tvout_write(tvout, 1, TVO_HD_DAC_CFG_OFF);
407}
408
409static const struct drm_encoder_helper_funcs sti_hda_encoder_helper_funcs = {
410 .dpms = sti_tvout_encoder_dpms,
411 .mode_fixup = sti_tvout_encoder_mode_fixup,
412 .mode_set = sti_tvout_encoder_mode_set,
413 .prepare = sti_tvout_encoder_prepare,
414 .commit = sti_hda_encoder_commit,
415 .disable = sti_hda_encoder_disable,
416};
417
418static struct drm_encoder *sti_tvout_create_hda_encoder(struct drm_device *dev,
419 struct sti_tvout *tvout)
420{
421 struct sti_tvout_encoder *encoder;
422 struct drm_encoder *drm_encoder;
423
424 encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);
425 if (!encoder)
426 return NULL;
427
428 encoder->tvout = tvout;
429
430 drm_encoder = (struct drm_encoder *) encoder;
431
432 drm_encoder->possible_crtcs = ENCODER_MAIN_CRTC_MASK;
433 drm_encoder->possible_clones = 1 << 0;
434
435 drm_encoder_init(dev, drm_encoder,
436 &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_DAC);
437
438 drm_encoder_helper_add(drm_encoder, &sti_hda_encoder_helper_funcs);
439
440 return drm_encoder;
441}
442
443static void sti_hdmi_encoder_commit(struct drm_encoder *encoder)
444{
445 struct sti_tvout *tvout = to_sti_tvout(encoder);
446
447 tvout_hdmi_start(tvout, true);
448}
449
450static void sti_hdmi_encoder_disable(struct drm_encoder *encoder)
451{
452 struct sti_tvout *tvout = to_sti_tvout(encoder);
453
454 /* reset VIP register */
455 tvout_write(tvout, 0x0, TVO_VIP_HDMI);
456}
457
458static const struct drm_encoder_helper_funcs sti_hdmi_encoder_helper_funcs = {
459 .dpms = sti_tvout_encoder_dpms,
460 .mode_fixup = sti_tvout_encoder_mode_fixup,
461 .mode_set = sti_tvout_encoder_mode_set,
462 .prepare = sti_tvout_encoder_prepare,
463 .commit = sti_hdmi_encoder_commit,
464 .disable = sti_hdmi_encoder_disable,
465};
466
467static struct drm_encoder *sti_tvout_create_hdmi_encoder(struct drm_device *dev,
468 struct sti_tvout *tvout)
469{
470 struct sti_tvout_encoder *encoder;
471 struct drm_encoder *drm_encoder;
472
473 encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);
474 if (!encoder)
475 return NULL;
476
477 encoder->tvout = tvout;
478
479 drm_encoder = (struct drm_encoder *) encoder;
480
481 drm_encoder->possible_crtcs = ENCODER_MAIN_CRTC_MASK;
482 drm_encoder->possible_clones = 1 << 1;
483
484 drm_encoder_init(dev, drm_encoder,
485 &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_TMDS);
486
487 drm_encoder_helper_add(drm_encoder, &sti_hdmi_encoder_helper_funcs);
488
489 return drm_encoder;
490}
491
492static void sti_tvout_create_encoders(struct drm_device *dev,
493 struct sti_tvout *tvout)
494{
495 tvout->hdmi = sti_tvout_create_hdmi_encoder(dev, tvout);
496 tvout->hda = sti_tvout_create_hda_encoder(dev, tvout);
497}
498
499static void sti_tvout_destroy_encoders(struct sti_tvout *tvout)
500{
501 if (tvout->hdmi)
502 drm_encoder_cleanup(tvout->hdmi);
503 tvout->hdmi = NULL;
504
505 if (tvout->hda)
506 drm_encoder_cleanup(tvout->hda);
507 tvout->hda = NULL;
508}
509
510static int sti_tvout_bind(struct device *dev, struct device *master, void *data)
511{
512 struct sti_tvout *tvout = dev_get_drvdata(dev);
513 struct drm_device *drm_dev = data;
514 unsigned int i;
515 int ret;
516
517 tvout->drm_dev = drm_dev;
518
519 /* set preformatter matrix */
520 for (i = 0; i < 8; i++) {
521 tvout_write(tvout, rgb_to_ycbcr_601[i],
522 TVO_CSC_MAIN_M0 + (i * 4));
523 tvout_write(tvout, rgb_to_ycbcr_601[i],
524 TVO_CSC_AUX_M0 + (i * 4));
525 }
526
527 sti_tvout_create_encoders(drm_dev, tvout);
528
529 ret = component_bind_all(dev, drm_dev);
530 if (ret)
531 sti_tvout_destroy_encoders(tvout);
532
533 return ret;
534}
535
536static void sti_tvout_unbind(struct device *dev, struct device *master,
537 void *data)
538{
539 /* do nothing */
540}
541
542static const struct component_ops sti_tvout_ops = {
543 .bind = sti_tvout_bind,
544 .unbind = sti_tvout_unbind,
545};
546
547static int compare_of(struct device *dev, void *data)
548{
549 return dev->of_node == data;
550}
551
552static int sti_tvout_master_bind(struct device *dev)
553{
554 return 0;
555}
556
557static void sti_tvout_master_unbind(struct device *dev)
558{
559 /* do nothing */
560}
561
562static const struct component_master_ops sti_tvout_master_ops = {
563 .bind = sti_tvout_master_bind,
564 .unbind = sti_tvout_master_unbind,
565};
566
567static int sti_tvout_probe(struct platform_device *pdev)
568{
569 struct device *dev = &pdev->dev;
570 struct device_node *node = dev->of_node;
571 struct sti_tvout *tvout;
572 struct resource *res;
573 struct device_node *child_np;
574 struct component_match *match = NULL;
575
576 DRM_INFO("%s\n", __func__);
577
578 if (!node)
579 return -ENODEV;
580
581 tvout = devm_kzalloc(dev, sizeof(*tvout), GFP_KERNEL);
582 if (!tvout)
583 return -ENOMEM;
584
585 tvout->dev = dev;
586
587 /* get Memory ressources */
588 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tvout-reg");
589 if (!res) {
590 DRM_ERROR("Invalid glue resource\n");
591 return -ENOMEM;
592 }
593 tvout->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
594 if (IS_ERR(tvout->regs))
595 return PTR_ERR(tvout->regs);
596
597 /* get reset resources */
598 tvout->reset = devm_reset_control_get(dev, "tvout");
599 /* take tvout out of reset */
600 if (!IS_ERR(tvout->reset))
601 reset_control_deassert(tvout->reset);
602
603 platform_set_drvdata(pdev, tvout);
604
605 of_platform_populate(node, NULL, NULL, dev);
606
607 child_np = of_get_next_available_child(node, NULL);
608
609 while (child_np) {
610 component_match_add(dev, &match, compare_of, child_np);
611 of_node_put(child_np);
612 child_np = of_get_next_available_child(node, child_np);
613 }
614
615 component_master_add_with_match(dev, &sti_tvout_master_ops, match);
616
617 return component_add(dev, &sti_tvout_ops);
618}
619
620static int sti_tvout_remove(struct platform_device *pdev)
621{
622 component_master_del(&pdev->dev, &sti_tvout_master_ops);
623 component_del(&pdev->dev, &sti_tvout_ops);
624 return 0;
625}
626
627static struct of_device_id tvout_of_match[] = {
628 { .compatible = "st,stih416-tvout", },
629 { .compatible = "st,stih407-tvout", },
630 { /* end node */ }
631};
632MODULE_DEVICE_TABLE(of, tvout_of_match);
633
634struct platform_driver sti_tvout_driver = {
635 .driver = {
636 .name = "sti-tvout",
637 .owner = THIS_MODULE,
638 .of_match_table = tvout_of_match,
639 },
640 .probe = sti_tvout_probe,
641 .remove = sti_tvout_remove,
642};
643
644module_platform_driver(sti_tvout_driver);
645
646MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
647MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
648MODULE_LICENSE("GPL");