xref: /openbmc/linux/drivers/gpu/drm/sti/sti_tvout.c (revision b96fc2f3)
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 #include "sti_crtc.h"
20 
21 /* glue registers */
22 #define TVO_CSC_MAIN_M0                  0x000
23 #define TVO_CSC_MAIN_M1                  0x004
24 #define TVO_CSC_MAIN_M2                  0x008
25 #define TVO_CSC_MAIN_M3                  0x00c
26 #define TVO_CSC_MAIN_M4                  0x010
27 #define TVO_CSC_MAIN_M5                  0x014
28 #define TVO_CSC_MAIN_M6                  0x018
29 #define TVO_CSC_MAIN_M7                  0x01c
30 #define TVO_MAIN_IN_VID_FORMAT           0x030
31 #define TVO_CSC_AUX_M0                   0x100
32 #define TVO_CSC_AUX_M1                   0x104
33 #define TVO_CSC_AUX_M2                   0x108
34 #define TVO_CSC_AUX_M3                   0x10c
35 #define TVO_CSC_AUX_M4                   0x110
36 #define TVO_CSC_AUX_M5                   0x114
37 #define TVO_CSC_AUX_M6                   0x118
38 #define TVO_CSC_AUX_M7                   0x11c
39 #define TVO_AUX_IN_VID_FORMAT            0x130
40 #define TVO_VIP_HDF                      0x400
41 #define TVO_HD_SYNC_SEL                  0x418
42 #define TVO_HD_DAC_CFG_OFF               0x420
43 #define TVO_VIP_HDMI                     0x500
44 #define TVO_HDMI_FORCE_COLOR_0           0x504
45 #define TVO_HDMI_FORCE_COLOR_1           0x508
46 #define TVO_HDMI_CLIP_VALUE_B_CB         0x50c
47 #define TVO_HDMI_CLIP_VALUE_Y_G          0x510
48 #define TVO_HDMI_CLIP_VALUE_R_CR         0x514
49 #define TVO_HDMI_SYNC_SEL                0x518
50 #define TVO_HDMI_DFV_OBS                 0x540
51 #define TVO_VIP_DVO                      0x600
52 #define TVO_DVO_SYNC_SEL                 0x618
53 #define TVO_DVO_CONFIG                   0x620
54 
55 #define TVO_IN_FMT_SIGNED                BIT(0)
56 #define TVO_SYNC_EXT                     BIT(4)
57 
58 #define TVO_VIP_REORDER_R_SHIFT          24
59 #define TVO_VIP_REORDER_G_SHIFT          20
60 #define TVO_VIP_REORDER_B_SHIFT          16
61 #define TVO_VIP_REORDER_MASK             0x3
62 #define TVO_VIP_REORDER_Y_G_SEL          0
63 #define TVO_VIP_REORDER_CB_B_SEL         1
64 #define TVO_VIP_REORDER_CR_R_SEL         2
65 
66 #define TVO_VIP_CLIP_SHIFT               8
67 #define TVO_VIP_CLIP_MASK                0x7
68 #define TVO_VIP_CLIP_DISABLED            0
69 #define TVO_VIP_CLIP_EAV_SAV             1
70 #define TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y 2
71 #define TVO_VIP_CLIP_LIMITED_RANGE_CB_CR 3
72 #define TVO_VIP_CLIP_PROG_RANGE          4
73 
74 #define TVO_VIP_RND_SHIFT                4
75 #define TVO_VIP_RND_MASK                 0x3
76 #define TVO_VIP_RND_8BIT_ROUNDED         0
77 #define TVO_VIP_RND_10BIT_ROUNDED        1
78 #define TVO_VIP_RND_12BIT_ROUNDED        2
79 
80 #define TVO_VIP_SEL_INPUT_MASK           0xf
81 #define TVO_VIP_SEL_INPUT_MAIN           0x0
82 #define TVO_VIP_SEL_INPUT_AUX            0x8
83 #define TVO_VIP_SEL_INPUT_FORCE_COLOR    0xf
84 #define TVO_VIP_SEL_INPUT_BYPASS_MASK    0x1
85 #define TVO_VIP_SEL_INPUT_BYPASSED       1
86 
87 #define TVO_SYNC_MAIN_VTG_SET_REF        0x00
88 #define TVO_SYNC_MAIN_VTG_SET_1          0x01
89 #define TVO_SYNC_MAIN_VTG_SET_2          0x02
90 #define TVO_SYNC_MAIN_VTG_SET_3          0x03
91 #define TVO_SYNC_MAIN_VTG_SET_4          0x04
92 #define TVO_SYNC_MAIN_VTG_SET_5          0x05
93 #define TVO_SYNC_MAIN_VTG_SET_6          0x06
94 #define TVO_SYNC_AUX_VTG_SET_REF         0x10
95 #define TVO_SYNC_AUX_VTG_SET_1           0x11
96 #define TVO_SYNC_AUX_VTG_SET_2           0x12
97 #define TVO_SYNC_AUX_VTG_SET_3           0x13
98 #define TVO_SYNC_AUX_VTG_SET_4           0x14
99 #define TVO_SYNC_AUX_VTG_SET_5           0x15
100 #define TVO_SYNC_AUX_VTG_SET_6           0x16
101 
102 #define TVO_SYNC_HD_DCS_SHIFT            8
103 
104 #define TVO_SYNC_DVO_PAD_HSYNC_SHIFT     8
105 #define TVO_SYNC_DVO_PAD_VSYNC_SHIFT     16
106 
107 #define ENCODER_CRTC_MASK                (BIT(0) | BIT(1))
108 
109 /* enum listing the supported output data format */
110 enum sti_tvout_video_out_type {
111 	STI_TVOUT_VIDEO_OUT_RGB,
112 	STI_TVOUT_VIDEO_OUT_YUV,
113 };
114 
115 struct sti_tvout {
116 	struct device *dev;
117 	struct drm_device *drm_dev;
118 	void __iomem *regs;
119 	struct reset_control *reset;
120 	struct drm_encoder *hdmi;
121 	struct drm_encoder *hda;
122 	struct drm_encoder *dvo;
123 };
124 
125 struct sti_tvout_encoder {
126 	struct drm_encoder encoder;
127 	struct sti_tvout *tvout;
128 };
129 
130 #define to_sti_tvout_encoder(x) \
131 	container_of(x, struct sti_tvout_encoder, encoder)
132 
133 #define to_sti_tvout(x) to_sti_tvout_encoder(x)->tvout
134 
135 /* preformatter conversion matrix */
136 static const u32 rgb_to_ycbcr_601[8] = {
137 	0xF927082E, 0x04C9FEAB, 0x01D30964, 0xFA95FD3D,
138 	0x0000082E, 0x00002000, 0x00002000, 0x00000000
139 };
140 
141 /* 709 RGB to YCbCr */
142 static const u32 rgb_to_ycbcr_709[8] = {
143 	0xF891082F, 0x0367FF40, 0x01280B71, 0xF9B1FE20,
144 	0x0000082F, 0x00002000, 0x00002000, 0x00000000
145 };
146 
147 static u32 tvout_read(struct sti_tvout *tvout, int offset)
148 {
149 	return readl(tvout->regs + offset);
150 }
151 
152 static void tvout_write(struct sti_tvout *tvout, u32 val, int offset)
153 {
154 	writel(val, tvout->regs + offset);
155 }
156 
157 /**
158  * Set the clipping mode of a VIP
159  *
160  * @tvout: tvout structure
161  * @reg: register to set
162  * @cr_r:
163  * @y_g:
164  * @cb_b:
165  */
166 static void tvout_vip_set_color_order(struct sti_tvout *tvout, int reg,
167 				      u32 cr_r, u32 y_g, u32 cb_b)
168 {
169 	u32 val = tvout_read(tvout, reg);
170 
171 	val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT);
172 	val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT);
173 	val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_B_SHIFT);
174 	val |= cr_r << TVO_VIP_REORDER_R_SHIFT;
175 	val |= y_g << TVO_VIP_REORDER_G_SHIFT;
176 	val |= cb_b << TVO_VIP_REORDER_B_SHIFT;
177 
178 	tvout_write(tvout, val, reg);
179 }
180 
181 /**
182  * Set the clipping mode of a VIP
183  *
184  * @tvout: tvout structure
185  * @reg: register to set
186  * @range: clipping range
187  */
188 static void tvout_vip_set_clip_mode(struct sti_tvout *tvout, int reg, u32 range)
189 {
190 	u32 val = tvout_read(tvout, reg);
191 
192 	val &= ~(TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT);
193 	val |= range << TVO_VIP_CLIP_SHIFT;
194 	tvout_write(tvout, val, reg);
195 }
196 
197 /**
198  * Set the rounded value of a VIP
199  *
200  * @tvout: tvout structure
201  * @reg: register to set
202  * @rnd: rounded val per component
203  */
204 static void tvout_vip_set_rnd(struct sti_tvout *tvout, int reg, u32 rnd)
205 {
206 	u32 val = tvout_read(tvout, reg);
207 
208 	val &= ~(TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT);
209 	val |= rnd << TVO_VIP_RND_SHIFT;
210 	tvout_write(tvout, val, reg);
211 }
212 
213 /**
214  * Select the VIP input
215  *
216  * @tvout: tvout structure
217  * @reg: register to set
218  * @main_path: main or auxiliary path
219  * @sel_input_logic_inverted: need to invert the logic
220  * @sel_input: selected_input (main/aux + conv)
221  */
222 static void tvout_vip_set_sel_input(struct sti_tvout *tvout,
223 				    int reg,
224 				    bool main_path,
225 				    bool sel_input_logic_inverted,
226 				    enum sti_tvout_video_out_type video_out)
227 {
228 	u32 sel_input;
229 	u32 val = tvout_read(tvout, reg);
230 
231 	if (main_path)
232 		sel_input = TVO_VIP_SEL_INPUT_MAIN;
233 	else
234 		sel_input = TVO_VIP_SEL_INPUT_AUX;
235 
236 	switch (video_out) {
237 	case STI_TVOUT_VIDEO_OUT_RGB:
238 		sel_input |= TVO_VIP_SEL_INPUT_BYPASSED;
239 		break;
240 	case STI_TVOUT_VIDEO_OUT_YUV:
241 		sel_input &= ~TVO_VIP_SEL_INPUT_BYPASSED;
242 		break;
243 	}
244 
245 	/* on stih407 chip the sel_input bypass mode logic is inverted */
246 	if (sel_input_logic_inverted)
247 		sel_input = sel_input ^ TVO_VIP_SEL_INPUT_BYPASS_MASK;
248 
249 	val &= ~TVO_VIP_SEL_INPUT_MASK;
250 	val |= sel_input;
251 	tvout_write(tvout, val, reg);
252 }
253 
254 /**
255  * Select the input video signed or unsigned
256  *
257  * @tvout: tvout structure
258  * @reg: register to set
259  * @in_vid_signed: used video input format
260  */
261 static void tvout_vip_set_in_vid_fmt(struct sti_tvout *tvout,
262 		int reg, u32 in_vid_fmt)
263 {
264 	u32 val = tvout_read(tvout, reg);
265 
266 	val &= ~TVO_IN_FMT_SIGNED;
267 	val |= in_vid_fmt;
268 	tvout_write(tvout, val, reg);
269 }
270 
271 /**
272  * Start VIP block for DVO output
273  *
274  * @tvout: pointer on tvout structure
275  * @main_path: true if main path has to be used in the vip configuration
276  *	  else aux path is used.
277  */
278 static void tvout_dvo_start(struct sti_tvout *tvout, bool main_path)
279 {
280 	struct device_node *node = tvout->dev->of_node;
281 	bool sel_input_logic_inverted = false;
282 	u32 tvo_in_vid_format;
283 	int val;
284 
285 	dev_dbg(tvout->dev, "%s\n", __func__);
286 
287 	if (main_path) {
288 		DRM_DEBUG_DRIVER("main vip for DVO\n");
289 		/* Select the input sync for dvo = VTG set 4 */
290 		val  = TVO_SYNC_MAIN_VTG_SET_4 << TVO_SYNC_DVO_PAD_VSYNC_SHIFT;
291 		val |= TVO_SYNC_MAIN_VTG_SET_4 << TVO_SYNC_DVO_PAD_HSYNC_SHIFT;
292 		val |= TVO_SYNC_MAIN_VTG_SET_4;
293 		tvout_write(tvout, val, TVO_DVO_SYNC_SEL);
294 		tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT;
295 	} else {
296 		DRM_DEBUG_DRIVER("aux vip for DVO\n");
297 		/* Select the input sync for dvo = VTG set 4 */
298 		val  = TVO_SYNC_AUX_VTG_SET_4 << TVO_SYNC_DVO_PAD_VSYNC_SHIFT;
299 		val |= TVO_SYNC_AUX_VTG_SET_4 << TVO_SYNC_DVO_PAD_HSYNC_SHIFT;
300 		val |= TVO_SYNC_AUX_VTG_SET_4;
301 		tvout_write(tvout, val, TVO_DVO_SYNC_SEL);
302 		tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT;
303 	}
304 
305 	/* Set color channel order */
306 	tvout_vip_set_color_order(tvout, TVO_VIP_DVO,
307 				  TVO_VIP_REORDER_CR_R_SEL,
308 				  TVO_VIP_REORDER_Y_G_SEL,
309 				  TVO_VIP_REORDER_CB_B_SEL);
310 
311 	/* Set clipping mode (Limited range RGB/Y) */
312 	tvout_vip_set_clip_mode(tvout, TVO_VIP_DVO,
313 				TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y);
314 
315 	/* Set round mode (rounded to 8-bit per component) */
316 	tvout_vip_set_rnd(tvout, TVO_VIP_DVO, TVO_VIP_RND_8BIT_ROUNDED);
317 
318 	if (of_device_is_compatible(node, "st,stih407-tvout")) {
319 		/* Set input video format */
320 		tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format,
321 					 TVO_IN_FMT_SIGNED);
322 		sel_input_logic_inverted = true;
323 	}
324 
325 	/* Input selection */
326 	tvout_vip_set_sel_input(tvout, TVO_VIP_DVO, main_path,
327 				sel_input_logic_inverted,
328 				STI_TVOUT_VIDEO_OUT_RGB);
329 }
330 
331 /**
332  * Start VIP block for HDMI output
333  *
334  * @tvout: pointer on tvout structure
335  * @main_path: true if main path has to be used in the vip configuration
336  *	  else aux path is used.
337  */
338 static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path)
339 {
340 	struct device_node *node = tvout->dev->of_node;
341 	bool sel_input_logic_inverted = false;
342 	u32 tvo_in_vid_format;
343 
344 	dev_dbg(tvout->dev, "%s\n", __func__);
345 
346 	if (main_path) {
347 		DRM_DEBUG_DRIVER("main vip for hdmi\n");
348 		/* select the input sync for hdmi = VTG set 1 */
349 		tvout_write(tvout, TVO_SYNC_MAIN_VTG_SET_1, TVO_HDMI_SYNC_SEL);
350 		tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT;
351 	} else {
352 		DRM_DEBUG_DRIVER("aux vip for hdmi\n");
353 		/* select the input sync for hdmi = VTG set 1 */
354 		tvout_write(tvout, TVO_SYNC_AUX_VTG_SET_1, TVO_HDMI_SYNC_SEL);
355 		tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT;
356 	}
357 
358 	/* set color channel order */
359 	tvout_vip_set_color_order(tvout, TVO_VIP_HDMI,
360 				  TVO_VIP_REORDER_CR_R_SEL,
361 				  TVO_VIP_REORDER_Y_G_SEL,
362 				  TVO_VIP_REORDER_CB_B_SEL);
363 
364 	/* set clipping mode (Limited range RGB/Y) */
365 	tvout_vip_set_clip_mode(tvout, TVO_VIP_HDMI,
366 			TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y);
367 
368 	/* set round mode (rounded to 8-bit per component) */
369 	tvout_vip_set_rnd(tvout, TVO_VIP_HDMI, TVO_VIP_RND_8BIT_ROUNDED);
370 
371 	if (of_device_is_compatible(node, "st,stih407-tvout")) {
372 		/* set input video format */
373 		tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format,
374 					TVO_IN_FMT_SIGNED);
375 		sel_input_logic_inverted = true;
376 	}
377 
378 	/* input selection */
379 	tvout_vip_set_sel_input(tvout, TVO_VIP_HDMI, main_path,
380 			sel_input_logic_inverted, STI_TVOUT_VIDEO_OUT_RGB);
381 }
382 
383 /**
384  * Start HDF VIP and HD DAC
385  *
386  * @tvout: pointer on tvout structure
387  * @main_path: true if main path has to be used in the vip configuration
388  *	  else aux path is used.
389  */
390 static void tvout_hda_start(struct sti_tvout *tvout, bool main_path)
391 {
392 	struct device_node *node = tvout->dev->of_node;
393 	bool sel_input_logic_inverted = false;
394 	u32 tvo_in_vid_format;
395 	int val;
396 
397 	dev_dbg(tvout->dev, "%s\n", __func__);
398 
399 	if (main_path) {
400 		val = TVO_SYNC_MAIN_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT;
401 		val |= TVO_SYNC_MAIN_VTG_SET_3;
402 		tvout_write(tvout, val, TVO_HD_SYNC_SEL);
403 		tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT;
404 	} else {
405 		val = TVO_SYNC_AUX_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT;
406 		val |= TVO_SYNC_AUX_VTG_SET_3;
407 		tvout_write(tvout, val, TVO_HD_SYNC_SEL);
408 		tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT;
409 	}
410 
411 	/* set color channel order */
412 	tvout_vip_set_color_order(tvout, TVO_VIP_HDF,
413 				  TVO_VIP_REORDER_CR_R_SEL,
414 				  TVO_VIP_REORDER_Y_G_SEL,
415 				  TVO_VIP_REORDER_CB_B_SEL);
416 
417 	/* set clipping mode (EAV/SAV clipping) */
418 	tvout_vip_set_clip_mode(tvout, TVO_VIP_HDF, TVO_VIP_CLIP_EAV_SAV);
419 
420 	/* set round mode (rounded to 10-bit per component) */
421 	tvout_vip_set_rnd(tvout, TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED);
422 
423 	if (of_device_is_compatible(node, "st,stih407-tvout")) {
424 		/* set input video format */
425 		tvout_vip_set_in_vid_fmt(tvout,
426 			tvo_in_vid_format, TVO_IN_FMT_SIGNED);
427 		sel_input_logic_inverted = true;
428 	}
429 
430 	/* Input selection */
431 	tvout_vip_set_sel_input(tvout, TVO_VIP_HDF, main_path,
432 				sel_input_logic_inverted,
433 				STI_TVOUT_VIDEO_OUT_YUV);
434 
435 	/* power up HD DAC */
436 	tvout_write(tvout, 0, TVO_HD_DAC_CFG_OFF);
437 }
438 
439 static void sti_tvout_encoder_dpms(struct drm_encoder *encoder, int mode)
440 {
441 }
442 
443 static bool sti_tvout_encoder_mode_fixup(struct drm_encoder *encoder,
444 				       const struct drm_display_mode *mode,
445 				       struct drm_display_mode *adjusted_mode)
446 {
447 	return true;
448 }
449 
450 static void sti_tvout_encoder_mode_set(struct drm_encoder *encoder,
451 				       struct drm_display_mode *mode,
452 				       struct drm_display_mode *adjusted_mode)
453 {
454 }
455 
456 static void sti_tvout_encoder_prepare(struct drm_encoder *encoder)
457 {
458 }
459 
460 static void sti_tvout_encoder_destroy(struct drm_encoder *encoder)
461 {
462 	struct sti_tvout_encoder *sti_encoder = to_sti_tvout_encoder(encoder);
463 
464 	drm_encoder_cleanup(encoder);
465 	kfree(sti_encoder);
466 }
467 
468 static const struct drm_encoder_funcs sti_tvout_encoder_funcs = {
469 	.destroy = sti_tvout_encoder_destroy,
470 };
471 
472 static void sti_dvo_encoder_commit(struct drm_encoder *encoder)
473 {
474 	struct sti_tvout *tvout = to_sti_tvout(encoder);
475 
476 	tvout_dvo_start(tvout, sti_crtc_is_main(encoder->crtc));
477 }
478 
479 static void sti_dvo_encoder_disable(struct drm_encoder *encoder)
480 {
481 	struct sti_tvout *tvout = to_sti_tvout(encoder);
482 
483 	/* Reset VIP register */
484 	tvout_write(tvout, 0x0, TVO_VIP_DVO);
485 }
486 
487 static const struct drm_encoder_helper_funcs sti_dvo_encoder_helper_funcs = {
488 	.dpms = sti_tvout_encoder_dpms,
489 	.mode_fixup = sti_tvout_encoder_mode_fixup,
490 	.mode_set = sti_tvout_encoder_mode_set,
491 	.prepare = sti_tvout_encoder_prepare,
492 	.commit = sti_dvo_encoder_commit,
493 	.disable = sti_dvo_encoder_disable,
494 };
495 
496 static struct drm_encoder *
497 sti_tvout_create_dvo_encoder(struct drm_device *dev,
498 			     struct sti_tvout *tvout)
499 {
500 	struct sti_tvout_encoder *encoder;
501 	struct drm_encoder *drm_encoder;
502 
503 	encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);
504 	if (!encoder)
505 		return NULL;
506 
507 	encoder->tvout = tvout;
508 
509 	drm_encoder = (struct drm_encoder *)encoder;
510 
511 	drm_encoder->possible_crtcs = ENCODER_CRTC_MASK;
512 	drm_encoder->possible_clones = 1 << 0;
513 
514 	drm_encoder_init(dev, drm_encoder,
515 			 &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_LVDS);
516 
517 	drm_encoder_helper_add(drm_encoder, &sti_dvo_encoder_helper_funcs);
518 
519 	return drm_encoder;
520 }
521 
522 static void sti_hda_encoder_commit(struct drm_encoder *encoder)
523 {
524 	struct sti_tvout *tvout = to_sti_tvout(encoder);
525 
526 	tvout_hda_start(tvout, sti_crtc_is_main(encoder->crtc));
527 }
528 
529 static void sti_hda_encoder_disable(struct drm_encoder *encoder)
530 {
531 	struct sti_tvout *tvout = to_sti_tvout(encoder);
532 
533 	/* reset VIP register */
534 	tvout_write(tvout, 0x0, TVO_VIP_HDF);
535 
536 	/* power down HD DAC */
537 	tvout_write(tvout, 1, TVO_HD_DAC_CFG_OFF);
538 }
539 
540 static const struct drm_encoder_helper_funcs sti_hda_encoder_helper_funcs = {
541 	.dpms = sti_tvout_encoder_dpms,
542 	.mode_fixup = sti_tvout_encoder_mode_fixup,
543 	.mode_set = sti_tvout_encoder_mode_set,
544 	.prepare = sti_tvout_encoder_prepare,
545 	.commit = sti_hda_encoder_commit,
546 	.disable = sti_hda_encoder_disable,
547 };
548 
549 static struct drm_encoder *sti_tvout_create_hda_encoder(struct drm_device *dev,
550 		struct sti_tvout *tvout)
551 {
552 	struct sti_tvout_encoder *encoder;
553 	struct drm_encoder *drm_encoder;
554 
555 	encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);
556 	if (!encoder)
557 		return NULL;
558 
559 	encoder->tvout = tvout;
560 
561 	drm_encoder = (struct drm_encoder *) encoder;
562 
563 	drm_encoder->possible_crtcs = ENCODER_CRTC_MASK;
564 	drm_encoder->possible_clones = 1 << 0;
565 
566 	drm_encoder_init(dev, drm_encoder,
567 			&sti_tvout_encoder_funcs, DRM_MODE_ENCODER_DAC);
568 
569 	drm_encoder_helper_add(drm_encoder, &sti_hda_encoder_helper_funcs);
570 
571 	return drm_encoder;
572 }
573 
574 static void sti_hdmi_encoder_commit(struct drm_encoder *encoder)
575 {
576 	struct sti_tvout *tvout = to_sti_tvout(encoder);
577 
578 	tvout_hdmi_start(tvout, sti_crtc_is_main(encoder->crtc));
579 }
580 
581 static void sti_hdmi_encoder_disable(struct drm_encoder *encoder)
582 {
583 	struct sti_tvout *tvout = to_sti_tvout(encoder);
584 
585 	/* reset VIP register */
586 	tvout_write(tvout, 0x0, TVO_VIP_HDMI);
587 }
588 
589 static const struct drm_encoder_helper_funcs sti_hdmi_encoder_helper_funcs = {
590 	.dpms = sti_tvout_encoder_dpms,
591 	.mode_fixup = sti_tvout_encoder_mode_fixup,
592 	.mode_set = sti_tvout_encoder_mode_set,
593 	.prepare = sti_tvout_encoder_prepare,
594 	.commit = sti_hdmi_encoder_commit,
595 	.disable = sti_hdmi_encoder_disable,
596 };
597 
598 static struct drm_encoder *sti_tvout_create_hdmi_encoder(struct drm_device *dev,
599 		struct sti_tvout *tvout)
600 {
601 	struct sti_tvout_encoder *encoder;
602 	struct drm_encoder *drm_encoder;
603 
604 	encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);
605 	if (!encoder)
606 		return NULL;
607 
608 	encoder->tvout = tvout;
609 
610 	drm_encoder = (struct drm_encoder *) encoder;
611 
612 	drm_encoder->possible_crtcs = ENCODER_CRTC_MASK;
613 	drm_encoder->possible_clones = 1 << 1;
614 
615 	drm_encoder_init(dev, drm_encoder,
616 			&sti_tvout_encoder_funcs, DRM_MODE_ENCODER_TMDS);
617 
618 	drm_encoder_helper_add(drm_encoder, &sti_hdmi_encoder_helper_funcs);
619 
620 	return drm_encoder;
621 }
622 
623 static void sti_tvout_create_encoders(struct drm_device *dev,
624 		struct sti_tvout *tvout)
625 {
626 	tvout->hdmi = sti_tvout_create_hdmi_encoder(dev, tvout);
627 	tvout->hda = sti_tvout_create_hda_encoder(dev, tvout);
628 	tvout->dvo = sti_tvout_create_dvo_encoder(dev, tvout);
629 }
630 
631 static void sti_tvout_destroy_encoders(struct sti_tvout *tvout)
632 {
633 	if (tvout->hdmi)
634 		drm_encoder_cleanup(tvout->hdmi);
635 	tvout->hdmi = NULL;
636 
637 	if (tvout->hda)
638 		drm_encoder_cleanup(tvout->hda);
639 	tvout->hda = NULL;
640 }
641 
642 static int sti_tvout_bind(struct device *dev, struct device *master, void *data)
643 {
644 	struct sti_tvout *tvout = dev_get_drvdata(dev);
645 	struct drm_device *drm_dev = data;
646 	unsigned int i;
647 
648 	tvout->drm_dev = drm_dev;
649 
650 	/* set preformatter matrix */
651 	for (i = 0; i < 8; i++) {
652 		tvout_write(tvout, rgb_to_ycbcr_601[i],
653 			TVO_CSC_MAIN_M0 + (i * 4));
654 		tvout_write(tvout, rgb_to_ycbcr_601[i],
655 			TVO_CSC_AUX_M0 + (i * 4));
656 	}
657 
658 	sti_tvout_create_encoders(drm_dev, tvout);
659 
660 	return 0;
661 }
662 
663 static void sti_tvout_unbind(struct device *dev, struct device *master,
664 	void *data)
665 {
666 	struct sti_tvout *tvout = dev_get_drvdata(dev);
667 
668 	sti_tvout_destroy_encoders(tvout);
669 }
670 
671 static const struct component_ops sti_tvout_ops = {
672 	.bind	= sti_tvout_bind,
673 	.unbind	= sti_tvout_unbind,
674 };
675 
676 static int sti_tvout_probe(struct platform_device *pdev)
677 {
678 	struct device *dev = &pdev->dev;
679 	struct device_node *node = dev->of_node;
680 	struct sti_tvout *tvout;
681 	struct resource *res;
682 
683 	DRM_INFO("%s\n", __func__);
684 
685 	if (!node)
686 		return -ENODEV;
687 
688 	tvout = devm_kzalloc(dev, sizeof(*tvout), GFP_KERNEL);
689 	if (!tvout)
690 		return -ENOMEM;
691 
692 	tvout->dev = dev;
693 
694 	/* get Memory ressources */
695 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tvout-reg");
696 	if (!res) {
697 		DRM_ERROR("Invalid glue resource\n");
698 		return -ENOMEM;
699 	}
700 	tvout->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
701 	if (!tvout->regs)
702 		return -ENOMEM;
703 
704 	/* get reset resources */
705 	tvout->reset = devm_reset_control_get(dev, "tvout");
706 	/* take tvout out of reset */
707 	if (!IS_ERR(tvout->reset))
708 		reset_control_deassert(tvout->reset);
709 
710 	platform_set_drvdata(pdev, tvout);
711 
712 	return component_add(dev, &sti_tvout_ops);
713 }
714 
715 static int sti_tvout_remove(struct platform_device *pdev)
716 {
717 	component_del(&pdev->dev, &sti_tvout_ops);
718 	return 0;
719 }
720 
721 static const struct of_device_id tvout_of_match[] = {
722 	{ .compatible = "st,stih416-tvout", },
723 	{ .compatible = "st,stih407-tvout", },
724 	{ /* end node */ }
725 };
726 MODULE_DEVICE_TABLE(of, tvout_of_match);
727 
728 struct platform_driver sti_tvout_driver = {
729 	.driver = {
730 		.name = "sti-tvout",
731 		.owner = THIS_MODULE,
732 		.of_match_table = tvout_of_match,
733 	},
734 	.probe = sti_tvout_probe,
735 	.remove = sti_tvout_remove,
736 };
737 
738 module_platform_driver(sti_tvout_driver);
739 
740 MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
741 MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
742 MODULE_LICENSE("GPL");
743