brintos

brintos / linux-shallow public Read only

0
0
Text · 24.6 KiB · e714c23 Raw
898 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) STMicroelectronics SA 20144 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>5 *          Vincent Abriou <vincent.abriou@st.com>6 *          for STMicroelectronics.7 */8 9#include <linux/clk.h>10#include <linux/component.h>11#include <linux/io.h>12#include <linux/module.h>13#include <linux/of_platform.h>14#include <linux/platform_device.h>15#include <linux/reset.h>16#include <linux/seq_file.h>17 18#include <drm/drm_atomic_helper.h>19#include <drm/drm_debugfs.h>20#include <drm/drm_device.h>21#include <drm/drm_file.h>22#include <drm/drm_print.h>23 24#include "sti_crtc.h"25#include "sti_drv.h"26#include "sti_vtg.h"27 28/* glue registers */29#define TVO_CSC_MAIN_M0                  0x00030#define TVO_CSC_MAIN_M1                  0x00431#define TVO_CSC_MAIN_M2                  0x00832#define TVO_CSC_MAIN_M3                  0x00c33#define TVO_CSC_MAIN_M4                  0x01034#define TVO_CSC_MAIN_M5                  0x01435#define TVO_CSC_MAIN_M6                  0x01836#define TVO_CSC_MAIN_M7                  0x01c37#define TVO_MAIN_IN_VID_FORMAT           0x03038#define TVO_CSC_AUX_M0                   0x10039#define TVO_CSC_AUX_M1                   0x10440#define TVO_CSC_AUX_M2                   0x10841#define TVO_CSC_AUX_M3                   0x10c42#define TVO_CSC_AUX_M4                   0x11043#define TVO_CSC_AUX_M5                   0x11444#define TVO_CSC_AUX_M6                   0x11845#define TVO_CSC_AUX_M7                   0x11c46#define TVO_AUX_IN_VID_FORMAT            0x13047#define TVO_VIP_HDF                      0x40048#define TVO_HD_SYNC_SEL                  0x41849#define TVO_HD_DAC_CFG_OFF               0x42050#define TVO_VIP_HDMI                     0x50051#define TVO_HDMI_FORCE_COLOR_0           0x50452#define TVO_HDMI_FORCE_COLOR_1           0x50853#define TVO_HDMI_CLIP_VALUE_B_CB         0x50c54#define TVO_HDMI_CLIP_VALUE_Y_G          0x51055#define TVO_HDMI_CLIP_VALUE_R_CR         0x51456#define TVO_HDMI_SYNC_SEL                0x51857#define TVO_HDMI_DFV_OBS                 0x54058#define TVO_VIP_DVO                      0x60059#define TVO_DVO_SYNC_SEL                 0x61860#define TVO_DVO_CONFIG                   0x62061 62#define TVO_IN_FMT_SIGNED                BIT(0)63#define TVO_SYNC_EXT                     BIT(4)64 65#define TVO_VIP_REORDER_R_SHIFT          2466#define TVO_VIP_REORDER_G_SHIFT          2067#define TVO_VIP_REORDER_B_SHIFT          1668#define TVO_VIP_REORDER_MASK             0x369#define TVO_VIP_REORDER_Y_G_SEL          070#define TVO_VIP_REORDER_CB_B_SEL         171#define TVO_VIP_REORDER_CR_R_SEL         272 73#define TVO_VIP_CLIP_SHIFT               874#define TVO_VIP_CLIP_MASK                0x775#define TVO_VIP_CLIP_DISABLED            076#define TVO_VIP_CLIP_EAV_SAV             177#define TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y 278#define TVO_VIP_CLIP_LIMITED_RANGE_CB_CR 379#define TVO_VIP_CLIP_PROG_RANGE          480 81#define TVO_VIP_RND_SHIFT                482#define TVO_VIP_RND_MASK                 0x383#define TVO_VIP_RND_8BIT_ROUNDED         084#define TVO_VIP_RND_10BIT_ROUNDED        185#define TVO_VIP_RND_12BIT_ROUNDED        286 87#define TVO_VIP_SEL_INPUT_MASK           0xf88#define TVO_VIP_SEL_INPUT_MAIN           0x089#define TVO_VIP_SEL_INPUT_AUX            0x890#define TVO_VIP_SEL_INPUT_FORCE_COLOR    0xf91#define TVO_VIP_SEL_INPUT_BYPASS_MASK    0x192#define TVO_VIP_SEL_INPUT_BYPASSED       193 94#define TVO_SYNC_MAIN_VTG_SET_REF        0x0095#define TVO_SYNC_AUX_VTG_SET_REF         0x1096 97#define TVO_SYNC_HD_DCS_SHIFT            898 99#define TVO_SYNC_DVO_PAD_HSYNC_SHIFT     8100#define TVO_SYNC_DVO_PAD_VSYNC_SHIFT     16101 102#define ENCODER_CRTC_MASK                (BIT(0) | BIT(1))103 104#define TVO_MIN_HD_HEIGHT                720105 106/* enum listing the supported output data format */107enum sti_tvout_video_out_type {108	STI_TVOUT_VIDEO_OUT_RGB,109	STI_TVOUT_VIDEO_OUT_YUV,110};111 112struct sti_tvout {113	struct device *dev;114	struct drm_device *drm_dev;115	void __iomem *regs;116	struct reset_control *reset;117	struct drm_encoder *hdmi;118	struct drm_encoder *hda;119	struct drm_encoder *dvo;120	bool debugfs_registered;121};122 123struct sti_tvout_encoder {124	struct drm_encoder encoder;125	struct sti_tvout *tvout;126};127 128#define to_sti_tvout_encoder(x) \129	container_of(x, struct sti_tvout_encoder, encoder)130 131#define to_sti_tvout(x) to_sti_tvout_encoder(x)->tvout132 133/* preformatter conversion matrix */134static const u32 rgb_to_ycbcr_601[8] = {135	0xF927082E, 0x04C9FEAB, 0x01D30964, 0xFA95FD3D,136	0x0000082E, 0x00002000, 0x00002000, 0x00000000137};138 139/* 709 RGB to YCbCr */140static const u32 rgb_to_ycbcr_709[8] = {141	0xF891082F, 0x0367FF40, 0x01280B71, 0xF9B1FE20,142	0x0000082F, 0x00002000, 0x00002000, 0x00000000143};144 145static u32 tvout_read(struct sti_tvout *tvout, int offset)146{147	return readl(tvout->regs + offset);148}149 150static void tvout_write(struct sti_tvout *tvout, u32 val, int offset)151{152	writel(val, tvout->regs + offset);153}154 155/**156 * tvout_vip_set_color_order - Set the clipping mode of a VIP157 *158 * @tvout: tvout structure159 * @reg: register to set160 * @cr_r: red chroma or red order161 * @y_g: y or green order162 * @cb_b: blue chroma or blue order163 */164static void tvout_vip_set_color_order(struct sti_tvout *tvout, int reg,165				      u32 cr_r, u32 y_g, u32 cb_b)166{167	u32 val = tvout_read(tvout, reg);168 169	val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT);170	val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT);171	val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_B_SHIFT);172	val |= cr_r << TVO_VIP_REORDER_R_SHIFT;173	val |= y_g << TVO_VIP_REORDER_G_SHIFT;174	val |= cb_b << TVO_VIP_REORDER_B_SHIFT;175 176	tvout_write(tvout, val, reg);177}178 179/**180 * tvout_vip_set_clip_mode - Set the clipping mode of a VIP181 *182 * @tvout: tvout structure183 * @reg: register to set184 * @range: clipping range185 */186static void tvout_vip_set_clip_mode(struct sti_tvout *tvout, int reg, u32 range)187{188	u32 val = tvout_read(tvout, reg);189 190	val &= ~(TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT);191	val |= range << TVO_VIP_CLIP_SHIFT;192	tvout_write(tvout, val, reg);193}194 195/**196 * tvout_vip_set_rnd - Set the rounded value of a VIP197 *198 * @tvout: tvout structure199 * @reg: register to set200 * @rnd: rounded val per component201 */202static void tvout_vip_set_rnd(struct sti_tvout *tvout, int reg, u32 rnd)203{204	u32 val = tvout_read(tvout, reg);205 206	val &= ~(TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT);207	val |= rnd << TVO_VIP_RND_SHIFT;208	tvout_write(tvout, val, reg);209}210 211/**212 * tvout_vip_set_sel_input - Select the VIP input213 *214 * @tvout: tvout structure215 * @reg: register to set216 * @main_path: main or auxiliary path217 * @video_out: selected_input (main/aux + conv)218 */219static void tvout_vip_set_sel_input(struct sti_tvout *tvout,220				    int reg,221				    bool main_path,222				    enum sti_tvout_video_out_type video_out)223{224	u32 sel_input;225	u32 val = tvout_read(tvout, reg);226 227	if (main_path)228		sel_input = TVO_VIP_SEL_INPUT_MAIN;229	else230		sel_input = TVO_VIP_SEL_INPUT_AUX;231 232	switch (video_out) {233	case STI_TVOUT_VIDEO_OUT_RGB:234		sel_input |= TVO_VIP_SEL_INPUT_BYPASSED;235		break;236	case STI_TVOUT_VIDEO_OUT_YUV:237		sel_input &= ~TVO_VIP_SEL_INPUT_BYPASSED;238		break;239	}240 241	/* on stih407 chip the sel_input bypass mode logic is inverted */242	sel_input = sel_input ^ TVO_VIP_SEL_INPUT_BYPASS_MASK;243 244	val &= ~TVO_VIP_SEL_INPUT_MASK;245	val |= sel_input;246	tvout_write(tvout, val, reg);247}248 249/**250 * tvout_vip_set_in_vid_fmt - Select the input video signed or unsigned251 *252 * @tvout: tvout structure253 * @reg: register to set254 * @in_vid_fmt: used video input format255 */256static void tvout_vip_set_in_vid_fmt(struct sti_tvout *tvout,257		int reg, u32 in_vid_fmt)258{259	u32 val = tvout_read(tvout, reg);260 261	val &= ~TVO_IN_FMT_SIGNED;262	val |= in_vid_fmt;263	tvout_write(tvout, val, reg);264}265 266/**267 * tvout_preformatter_set_matrix - Set preformatter matrix268 *269 * @tvout: tvout structure270 * @mode: display mode structure271 */272static void tvout_preformatter_set_matrix(struct sti_tvout *tvout,273					  struct drm_display_mode *mode)274{275	unsigned int i;276	const u32 *pf_matrix;277 278	if (mode->vdisplay >= TVO_MIN_HD_HEIGHT)279		pf_matrix = rgb_to_ycbcr_709;280	else281		pf_matrix = rgb_to_ycbcr_601;282 283	for (i = 0; i < 8; i++) {284		tvout_write(tvout, *(pf_matrix + i),285			    TVO_CSC_MAIN_M0 + (i * 4));286		tvout_write(tvout, *(pf_matrix + i),287			    TVO_CSC_AUX_M0 + (i * 4));288	}289}290 291/**292 * tvout_dvo_start - Start VIP block for DVO output293 *294 * @tvout: pointer on tvout structure295 * @main_path: true if main path has to be used in the vip configuration296 *	  else aux path is used.297 */298static void tvout_dvo_start(struct sti_tvout *tvout, bool main_path)299{300	u32 tvo_in_vid_format;301	int val, tmp;302 303	dev_dbg(tvout->dev, "%s\n", __func__);304 305	if (main_path) {306		DRM_DEBUG_DRIVER("main vip for DVO\n");307		/* Select the input sync for dvo */308		tmp = TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_DVO;309		val  = tmp << TVO_SYNC_DVO_PAD_VSYNC_SHIFT;310		val |= tmp << TVO_SYNC_DVO_PAD_HSYNC_SHIFT;311		val |= tmp;312		tvout_write(tvout, val, TVO_DVO_SYNC_SEL);313		tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT;314	} else {315		DRM_DEBUG_DRIVER("aux vip for DVO\n");316		/* Select the input sync for dvo */317		tmp = TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_DVO;318		val  = tmp << TVO_SYNC_DVO_PAD_VSYNC_SHIFT;319		val |= tmp << TVO_SYNC_DVO_PAD_HSYNC_SHIFT;320		val |= tmp;321		tvout_write(tvout, val, TVO_DVO_SYNC_SEL);322		tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT;323	}324 325	/* Set color channel order */326	tvout_vip_set_color_order(tvout, TVO_VIP_DVO,327				  TVO_VIP_REORDER_CR_R_SEL,328				  TVO_VIP_REORDER_Y_G_SEL,329				  TVO_VIP_REORDER_CB_B_SEL);330 331	/* Set clipping mode */332	tvout_vip_set_clip_mode(tvout, TVO_VIP_DVO, TVO_VIP_CLIP_DISABLED);333 334	/* Set round mode (rounded to 8-bit per component) */335	tvout_vip_set_rnd(tvout, TVO_VIP_DVO, TVO_VIP_RND_8BIT_ROUNDED);336 337	/* Set input video format */338	tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED);339 340	/* Input selection */341	tvout_vip_set_sel_input(tvout, TVO_VIP_DVO, main_path,342				STI_TVOUT_VIDEO_OUT_RGB);343}344 345/**346 * tvout_hdmi_start - Start VIP block for HDMI output347 *348 * @tvout: pointer on tvout structure349 * @main_path: true if main path has to be used in the vip configuration350 *	  else aux path is used.351 */352static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path)353{354	u32 tvo_in_vid_format;355 356	dev_dbg(tvout->dev, "%s\n", __func__);357 358	if (main_path) {359		DRM_DEBUG_DRIVER("main vip for hdmi\n");360		/* select the input sync for hdmi */361		tvout_write(tvout,362			    TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDMI,363			    TVO_HDMI_SYNC_SEL);364		tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT;365	} else {366		DRM_DEBUG_DRIVER("aux vip for hdmi\n");367		/* select the input sync for hdmi */368		tvout_write(tvout,369			    TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDMI,370			    TVO_HDMI_SYNC_SEL);371		tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT;372	}373 374	/* set color channel order */375	tvout_vip_set_color_order(tvout, TVO_VIP_HDMI,376				  TVO_VIP_REORDER_CR_R_SEL,377				  TVO_VIP_REORDER_Y_G_SEL,378				  TVO_VIP_REORDER_CB_B_SEL);379 380	/* set clipping mode */381	tvout_vip_set_clip_mode(tvout, TVO_VIP_HDMI, TVO_VIP_CLIP_DISABLED);382 383	/* set round mode (rounded to 8-bit per component) */384	tvout_vip_set_rnd(tvout, TVO_VIP_HDMI, TVO_VIP_RND_8BIT_ROUNDED);385 386	/* set input video format */387	tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED);388 389	/* input selection */390	tvout_vip_set_sel_input(tvout, TVO_VIP_HDMI, main_path,391				STI_TVOUT_VIDEO_OUT_RGB);392}393 394/**395 * tvout_hda_start - Start HDF VIP and HD DAC396 *397 * @tvout: pointer on tvout structure398 * @main_path: true if main path has to be used in the vip configuration399 *	  else aux path is used.400 */401static void tvout_hda_start(struct sti_tvout *tvout, bool main_path)402{403	u32 tvo_in_vid_format;404	int val;405 406	dev_dbg(tvout->dev, "%s\n", __func__);407 408	if (main_path) {409		DRM_DEBUG_DRIVER("main vip for HDF\n");410		/* Select the input sync for HD analog and HD DCS */411		val  = TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDDCS;412		val  = val << TVO_SYNC_HD_DCS_SHIFT;413		val |= TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDF;414		tvout_write(tvout, val, TVO_HD_SYNC_SEL);415		tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT;416	} else {417		DRM_DEBUG_DRIVER("aux vip for HDF\n");418		/* Select the input sync for HD analog and HD DCS */419		val  = TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDDCS;420		val  = val << TVO_SYNC_HD_DCS_SHIFT;421		val |= TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDF;422		tvout_write(tvout, val, TVO_HD_SYNC_SEL);423		tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT;424	}425 426	/* set color channel order */427	tvout_vip_set_color_order(tvout, TVO_VIP_HDF,428				  TVO_VIP_REORDER_CR_R_SEL,429				  TVO_VIP_REORDER_Y_G_SEL,430				  TVO_VIP_REORDER_CB_B_SEL);431 432	/* set clipping mode */433	tvout_vip_set_clip_mode(tvout, TVO_VIP_HDF, TVO_VIP_CLIP_DISABLED);434 435	/* set round mode (rounded to 10-bit per component) */436	tvout_vip_set_rnd(tvout, TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED);437 438	/* Set input video format */439	tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED);440 441	/* Input selection */442	tvout_vip_set_sel_input(tvout, TVO_VIP_HDF, main_path,443				STI_TVOUT_VIDEO_OUT_YUV);444 445	/* power up HD DAC */446	tvout_write(tvout, 0, TVO_HD_DAC_CFG_OFF);447}448 449#define DBGFS_DUMP(reg) seq_printf(s, "\n  %-25s 0x%08X", #reg, \450				   readl(tvout->regs + reg))451 452static void tvout_dbg_vip(struct seq_file *s, int val)453{454	int r, g, b, tmp, mask;455	char *const reorder[] = {"Y_G", "Cb_B", "Cr_R"};456	char *const clipping[] = {"No", "EAV/SAV", "Limited range RGB/Y",457				  "Limited range Cb/Cr", "decided by register"};458	char *const round[] = {"8-bit", "10-bit", "12-bit"};459	char *const input_sel[] = {"Main (color matrix enabled)",460				   "Main (color matrix by-passed)",461				   "", "", "", "", "", "",462				   "Aux (color matrix enabled)",463				   "Aux (color matrix by-passed)",464				   "", "", "", "", "", "Force value"};465 466	seq_putc(s, '\t');467	mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT;468	r = (val & mask) >> TVO_VIP_REORDER_R_SHIFT;469	mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT;470	g = (val & mask) >> TVO_VIP_REORDER_G_SHIFT;471	mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_B_SHIFT;472	b = (val & mask) >> TVO_VIP_REORDER_B_SHIFT;473	seq_printf(s, "%-24s %s->%s %s->%s %s->%s\n", "Reorder:",474		   reorder[r], reorder[TVO_VIP_REORDER_CR_R_SEL],475		   reorder[g], reorder[TVO_VIP_REORDER_Y_G_SEL],476		   reorder[b], reorder[TVO_VIP_REORDER_CB_B_SEL]);477	seq_puts(s, "\t\t\t\t\t");478	mask = TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT;479	tmp = (val & mask) >> TVO_VIP_CLIP_SHIFT;480	seq_printf(s, "%-24s %s\n", "Clipping:", clipping[tmp]);481	seq_puts(s, "\t\t\t\t\t");482	mask = TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT;483	tmp = (val & mask) >> TVO_VIP_RND_SHIFT;484	seq_printf(s, "%-24s input data rounded to %s per component\n",485		   "Round:", round[tmp]);486	seq_puts(s, "\t\t\t\t\t");487	tmp = (val & TVO_VIP_SEL_INPUT_MASK);488	seq_printf(s, "%-24s %s", "Input selection:", input_sel[tmp]);489}490 491static void tvout_dbg_hd_dac_cfg(struct seq_file *s, int val)492{493	seq_printf(s, "\t%-24s %s", "HD DAC:",494		   val & 1 ? "disabled" : "enabled");495}496 497static int tvout_dbg_show(struct seq_file *s, void *data)498{499	struct drm_info_node *node = s->private;500	struct sti_tvout *tvout = (struct sti_tvout *)node->info_ent->data;501	struct drm_crtc *crtc;502 503	seq_printf(s, "TVOUT: (vaddr = 0x%p)", tvout->regs);504 505	seq_puts(s, "\n\n  HDMI encoder: ");506	crtc = tvout->hdmi->crtc;507	if (crtc) {508		seq_printf(s, "connected to %s path",509			   sti_crtc_is_main(crtc) ? "main" : "aux");510		DBGFS_DUMP(TVO_HDMI_SYNC_SEL);511		DBGFS_DUMP(TVO_VIP_HDMI);512		tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_HDMI));513	} else {514		seq_puts(s, "disabled");515	}516 517	seq_puts(s, "\n\n  DVO encoder: ");518	crtc = tvout->dvo->crtc;519	if (crtc) {520		seq_printf(s, "connected to %s path",521			   sti_crtc_is_main(crtc) ? "main" : "aux");522		DBGFS_DUMP(TVO_DVO_SYNC_SEL);523		DBGFS_DUMP(TVO_DVO_CONFIG);524		DBGFS_DUMP(TVO_VIP_DVO);525		tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_DVO));526	} else {527		seq_puts(s, "disabled");528	}529 530	seq_puts(s, "\n\n  HDA encoder: ");531	crtc = tvout->hda->crtc;532	if (crtc) {533		seq_printf(s, "connected to %s path",534			   sti_crtc_is_main(crtc) ? "main" : "aux");535		DBGFS_DUMP(TVO_HD_SYNC_SEL);536		DBGFS_DUMP(TVO_HD_DAC_CFG_OFF);537		tvout_dbg_hd_dac_cfg(s,538				     readl(tvout->regs + TVO_HD_DAC_CFG_OFF));539		DBGFS_DUMP(TVO_VIP_HDF);540		tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_HDF));541	} else {542		seq_puts(s, "disabled");543	}544 545	seq_puts(s, "\n\n  main path configuration");546	DBGFS_DUMP(TVO_CSC_MAIN_M0);547	DBGFS_DUMP(TVO_CSC_MAIN_M1);548	DBGFS_DUMP(TVO_CSC_MAIN_M2);549	DBGFS_DUMP(TVO_CSC_MAIN_M3);550	DBGFS_DUMP(TVO_CSC_MAIN_M4);551	DBGFS_DUMP(TVO_CSC_MAIN_M5);552	DBGFS_DUMP(TVO_CSC_MAIN_M6);553	DBGFS_DUMP(TVO_CSC_MAIN_M7);554	DBGFS_DUMP(TVO_MAIN_IN_VID_FORMAT);555 556	seq_puts(s, "\n\n  auxiliary path configuration");557	DBGFS_DUMP(TVO_CSC_AUX_M0);558	DBGFS_DUMP(TVO_CSC_AUX_M2);559	DBGFS_DUMP(TVO_CSC_AUX_M3);560	DBGFS_DUMP(TVO_CSC_AUX_M4);561	DBGFS_DUMP(TVO_CSC_AUX_M5);562	DBGFS_DUMP(TVO_CSC_AUX_M6);563	DBGFS_DUMP(TVO_CSC_AUX_M7);564	DBGFS_DUMP(TVO_AUX_IN_VID_FORMAT);565	seq_putc(s, '\n');566	return 0;567}568 569static struct drm_info_list tvout_debugfs_files[] = {570	{ "tvout", tvout_dbg_show, 0, NULL },571};572 573static void tvout_debugfs_init(struct sti_tvout *tvout, struct drm_minor *minor)574{575	unsigned int i;576 577	for (i = 0; i < ARRAY_SIZE(tvout_debugfs_files); i++)578		tvout_debugfs_files[i].data = tvout;579 580	drm_debugfs_create_files(tvout_debugfs_files,581				 ARRAY_SIZE(tvout_debugfs_files),582				 minor->debugfs_root, minor);583}584 585static void sti_tvout_encoder_dpms(struct drm_encoder *encoder, int mode)586{587}588 589static void sti_tvout_encoder_mode_set(struct drm_encoder *encoder,590				       struct drm_display_mode *mode,591				       struct drm_display_mode *adjusted_mode)592{593}594 595static void sti_tvout_encoder_destroy(struct drm_encoder *encoder)596{597	struct sti_tvout_encoder *sti_encoder = to_sti_tvout_encoder(encoder);598 599	drm_encoder_cleanup(encoder);600	kfree(sti_encoder);601}602 603static int sti_tvout_late_register(struct drm_encoder *encoder)604{605	struct sti_tvout *tvout = to_sti_tvout(encoder);606 607	if (tvout->debugfs_registered)608		return 0;609 610	tvout_debugfs_init(tvout, encoder->dev->primary);611 612	tvout->debugfs_registered = true;613	return 0;614}615 616static void sti_tvout_early_unregister(struct drm_encoder *encoder)617{618	struct sti_tvout *tvout = to_sti_tvout(encoder);619 620	if (!tvout->debugfs_registered)621		return;622 623	tvout->debugfs_registered = false;624}625 626static const struct drm_encoder_funcs sti_tvout_encoder_funcs = {627	.destroy = sti_tvout_encoder_destroy,628	.late_register = sti_tvout_late_register,629	.early_unregister = sti_tvout_early_unregister,630};631 632static void sti_dvo_encoder_enable(struct drm_encoder *encoder)633{634	struct sti_tvout *tvout = to_sti_tvout(encoder);635 636	tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode);637 638	tvout_dvo_start(tvout, sti_crtc_is_main(encoder->crtc));639}640 641static void sti_dvo_encoder_disable(struct drm_encoder *encoder)642{643	struct sti_tvout *tvout = to_sti_tvout(encoder);644 645	/* Reset VIP register */646	tvout_write(tvout, 0x0, TVO_VIP_DVO);647}648 649static const struct drm_encoder_helper_funcs sti_dvo_encoder_helper_funcs = {650	.dpms = sti_tvout_encoder_dpms,651	.mode_set = sti_tvout_encoder_mode_set,652	.enable = sti_dvo_encoder_enable,653	.disable = sti_dvo_encoder_disable,654};655 656static struct drm_encoder *657sti_tvout_create_dvo_encoder(struct drm_device *dev,658			     struct sti_tvout *tvout)659{660	struct sti_tvout_encoder *encoder;661	struct drm_encoder *drm_encoder;662 663	encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);664	if (!encoder)665		return NULL;666 667	encoder->tvout = tvout;668 669	drm_encoder = &encoder->encoder;670 671	drm_encoder->possible_crtcs = ENCODER_CRTC_MASK;672 673	drm_encoder_init(dev, drm_encoder,674			 &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_LVDS,675			 NULL);676 677	drm_encoder_helper_add(drm_encoder, &sti_dvo_encoder_helper_funcs);678 679	return drm_encoder;680}681 682static void sti_hda_encoder_enable(struct drm_encoder *encoder)683{684	struct sti_tvout *tvout = to_sti_tvout(encoder);685 686	tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode);687 688	tvout_hda_start(tvout, sti_crtc_is_main(encoder->crtc));689}690 691static void sti_hda_encoder_disable(struct drm_encoder *encoder)692{693	struct sti_tvout *tvout = to_sti_tvout(encoder);694 695	/* reset VIP register */696	tvout_write(tvout, 0x0, TVO_VIP_HDF);697 698	/* power down HD DAC */699	tvout_write(tvout, 1, TVO_HD_DAC_CFG_OFF);700}701 702static const struct drm_encoder_helper_funcs sti_hda_encoder_helper_funcs = {703	.dpms = sti_tvout_encoder_dpms,704	.mode_set = sti_tvout_encoder_mode_set,705	.commit = sti_hda_encoder_enable,706	.disable = sti_hda_encoder_disable,707};708 709static struct drm_encoder *sti_tvout_create_hda_encoder(struct drm_device *dev,710		struct sti_tvout *tvout)711{712	struct sti_tvout_encoder *encoder;713	struct drm_encoder *drm_encoder;714 715	encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);716	if (!encoder)717		return NULL;718 719	encoder->tvout = tvout;720 721	drm_encoder = &encoder->encoder;722 723	drm_encoder->possible_crtcs = ENCODER_CRTC_MASK;724 725	drm_encoder_init(dev, drm_encoder,726			&sti_tvout_encoder_funcs, DRM_MODE_ENCODER_DAC, NULL);727 728	drm_encoder_helper_add(drm_encoder, &sti_hda_encoder_helper_funcs);729 730	return drm_encoder;731}732 733static void sti_hdmi_encoder_enable(struct drm_encoder *encoder)734{735	struct sti_tvout *tvout = to_sti_tvout(encoder);736 737	tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode);738 739	tvout_hdmi_start(tvout, sti_crtc_is_main(encoder->crtc));740}741 742static void sti_hdmi_encoder_disable(struct drm_encoder *encoder)743{744	struct sti_tvout *tvout = to_sti_tvout(encoder);745 746	/* reset VIP register */747	tvout_write(tvout, 0x0, TVO_VIP_HDMI);748}749 750static const struct drm_encoder_helper_funcs sti_hdmi_encoder_helper_funcs = {751	.dpms = sti_tvout_encoder_dpms,752	.mode_set = sti_tvout_encoder_mode_set,753	.commit = sti_hdmi_encoder_enable,754	.disable = sti_hdmi_encoder_disable,755};756 757static struct drm_encoder *sti_tvout_create_hdmi_encoder(struct drm_device *dev,758		struct sti_tvout *tvout)759{760	struct sti_tvout_encoder *encoder;761	struct drm_encoder *drm_encoder;762 763	encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);764	if (!encoder)765		return NULL;766 767	encoder->tvout = tvout;768 769	drm_encoder = &encoder->encoder;770 771	drm_encoder->possible_crtcs = ENCODER_CRTC_MASK;772 773	drm_encoder_init(dev, drm_encoder,774			&sti_tvout_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL);775 776	drm_encoder_helper_add(drm_encoder, &sti_hdmi_encoder_helper_funcs);777 778	return drm_encoder;779}780 781static void sti_tvout_create_encoders(struct drm_device *dev,782		struct sti_tvout *tvout)783{784	tvout->hdmi = sti_tvout_create_hdmi_encoder(dev, tvout);785	tvout->hda = sti_tvout_create_hda_encoder(dev, tvout);786	tvout->dvo = sti_tvout_create_dvo_encoder(dev, tvout);787 788	tvout->hdmi->possible_clones = drm_encoder_mask(tvout->hdmi) |789		drm_encoder_mask(tvout->hda) | drm_encoder_mask(tvout->dvo);790	tvout->hda->possible_clones = drm_encoder_mask(tvout->hdmi) |791		drm_encoder_mask(tvout->hda) | drm_encoder_mask(tvout->dvo);792	tvout->dvo->possible_clones = drm_encoder_mask(tvout->hdmi) |793		drm_encoder_mask(tvout->hda) | drm_encoder_mask(tvout->dvo);794}795 796static void sti_tvout_destroy_encoders(struct sti_tvout *tvout)797{798	if (tvout->hdmi)799		drm_encoder_cleanup(tvout->hdmi);800	tvout->hdmi = NULL;801 802	if (tvout->hda)803		drm_encoder_cleanup(tvout->hda);804	tvout->hda = NULL;805 806	if (tvout->dvo)807		drm_encoder_cleanup(tvout->dvo);808	tvout->dvo = NULL;809}810 811static int sti_tvout_bind(struct device *dev, struct device *master, void *data)812{813	struct sti_tvout *tvout = dev_get_drvdata(dev);814	struct drm_device *drm_dev = data;815 816	tvout->drm_dev = drm_dev;817 818	sti_tvout_create_encoders(drm_dev, tvout);819 820	return 0;821}822 823static void sti_tvout_unbind(struct device *dev, struct device *master,824	void *data)825{826	struct sti_tvout *tvout = dev_get_drvdata(dev);827 828	sti_tvout_destroy_encoders(tvout);829}830 831static const struct component_ops sti_tvout_ops = {832	.bind	= sti_tvout_bind,833	.unbind	= sti_tvout_unbind,834};835 836static int sti_tvout_probe(struct platform_device *pdev)837{838	struct device *dev = &pdev->dev;839	struct device_node *node = dev->of_node;840	struct sti_tvout *tvout;841	struct resource *res;842 843	DRM_INFO("%s\n", __func__);844 845	if (!node)846		return -ENODEV;847 848	tvout = devm_kzalloc(dev, sizeof(*tvout), GFP_KERNEL);849	if (!tvout)850		return -ENOMEM;851 852	tvout->dev = dev;853 854	/* get memory resources */855	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tvout-reg");856	if (!res) {857		DRM_ERROR("Invalid glue resource\n");858		return -ENOMEM;859	}860	tvout->regs = devm_ioremap(dev, res->start, resource_size(res));861	if (!tvout->regs)862		return -ENOMEM;863 864	/* get reset resources */865	tvout->reset = devm_reset_control_get(dev, "tvout");866	/* take tvout out of reset */867	if (!IS_ERR(tvout->reset))868		reset_control_deassert(tvout->reset);869 870	platform_set_drvdata(pdev, tvout);871 872	return component_add(dev, &sti_tvout_ops);873}874 875static void sti_tvout_remove(struct platform_device *pdev)876{877	component_del(&pdev->dev, &sti_tvout_ops);878}879 880static const struct of_device_id tvout_of_match[] = {881	{ .compatible = "st,stih407-tvout", },882	{ /* end node */ }883};884MODULE_DEVICE_TABLE(of, tvout_of_match);885 886struct platform_driver sti_tvout_driver = {887	.driver = {888		.name = "sti-tvout",889		.of_match_table = tvout_of_match,890	},891	.probe = sti_tvout_probe,892	.remove_new = sti_tvout_remove,893};894 895MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");896MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");897MODULE_LICENSE("GPL");898