cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sun4i_tv.c (17926B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright (C) 2015 Free Electrons
      4 * Copyright (C) 2015 NextThing Co
      5 *
      6 * Maxime Ripard <maxime.ripard@free-electrons.com>
      7 */
      8
      9#include <linux/clk.h>
     10#include <linux/component.h>
     11#include <linux/module.h>
     12#include <linux/of_address.h>
     13#include <linux/platform_device.h>
     14#include <linux/regmap.h>
     15#include <linux/reset.h>
     16
     17#include <drm/drm_atomic_helper.h>
     18#include <drm/drm_of.h>
     19#include <drm/drm_panel.h>
     20#include <drm/drm_print.h>
     21#include <drm/drm_probe_helper.h>
     22#include <drm/drm_simple_kms_helper.h>
     23
     24#include "sun4i_crtc.h"
     25#include "sun4i_drv.h"
     26#include "sunxi_engine.h"
     27
     28#define SUN4I_TVE_EN_REG		0x000
     29#define SUN4I_TVE_EN_DAC_MAP_MASK		GENMASK(19, 4)
     30#define SUN4I_TVE_EN_DAC_MAP(dac, out)		(((out) & 0xf) << (dac + 1) * 4)
     31#define SUN4I_TVE_EN_ENABLE			BIT(0)
     32
     33#define SUN4I_TVE_CFG0_REG		0x004
     34#define SUN4I_TVE_CFG0_DAC_CONTROL_54M		BIT(26)
     35#define SUN4I_TVE_CFG0_CORE_DATAPATH_54M	BIT(25)
     36#define SUN4I_TVE_CFG0_CORE_CONTROL_54M		BIT(24)
     37#define SUN4I_TVE_CFG0_YC_EN			BIT(17)
     38#define SUN4I_TVE_CFG0_COMP_EN			BIT(16)
     39#define SUN4I_TVE_CFG0_RES(x)			((x) & 0xf)
     40#define SUN4I_TVE_CFG0_RES_480i			SUN4I_TVE_CFG0_RES(0)
     41#define SUN4I_TVE_CFG0_RES_576i			SUN4I_TVE_CFG0_RES(1)
     42
     43#define SUN4I_TVE_DAC0_REG		0x008
     44#define SUN4I_TVE_DAC0_CLOCK_INVERT		BIT(24)
     45#define SUN4I_TVE_DAC0_LUMA(x)			(((x) & 3) << 20)
     46#define SUN4I_TVE_DAC0_LUMA_0_4			SUN4I_TVE_DAC0_LUMA(3)
     47#define SUN4I_TVE_DAC0_CHROMA(x)		(((x) & 3) << 18)
     48#define SUN4I_TVE_DAC0_CHROMA_0_75		SUN4I_TVE_DAC0_CHROMA(3)
     49#define SUN4I_TVE_DAC0_INTERNAL_DAC(x)		(((x) & 3) << 16)
     50#define SUN4I_TVE_DAC0_INTERNAL_DAC_37_5_OHMS	SUN4I_TVE_DAC0_INTERNAL_DAC(3)
     51#define SUN4I_TVE_DAC0_DAC_EN(dac)		BIT(dac)
     52
     53#define SUN4I_TVE_NOTCH_REG		0x00c
     54#define SUN4I_TVE_NOTCH_DAC0_TO_DAC_DLY(dac, x)	((4 - (x)) << (dac * 3))
     55
     56#define SUN4I_TVE_CHROMA_FREQ_REG	0x010
     57
     58#define SUN4I_TVE_PORCH_REG		0x014
     59#define SUN4I_TVE_PORCH_BACK(x)			((x) << 16)
     60#define SUN4I_TVE_PORCH_FRONT(x)		(x)
     61
     62#define SUN4I_TVE_LINE_REG		0x01c
     63#define SUN4I_TVE_LINE_FIRST(x)			((x) << 16)
     64#define SUN4I_TVE_LINE_NUMBER(x)		(x)
     65
     66#define SUN4I_TVE_LEVEL_REG		0x020
     67#define SUN4I_TVE_LEVEL_BLANK(x)		((x) << 16)
     68#define SUN4I_TVE_LEVEL_BLACK(x)		(x)
     69
     70#define SUN4I_TVE_DAC1_REG		0x024
     71#define SUN4I_TVE_DAC1_AMPLITUDE(dac, x)	((x) << (dac * 8))
     72
     73#define SUN4I_TVE_DETECT_STA_REG	0x038
     74#define SUN4I_TVE_DETECT_STA_DAC(dac)		BIT((dac * 8))
     75#define SUN4I_TVE_DETECT_STA_UNCONNECTED		0
     76#define SUN4I_TVE_DETECT_STA_CONNECTED			1
     77#define SUN4I_TVE_DETECT_STA_GROUND			2
     78
     79#define SUN4I_TVE_CB_CR_LVL_REG		0x10c
     80#define SUN4I_TVE_CB_CR_LVL_CR_BURST(x)		((x) << 8)
     81#define SUN4I_TVE_CB_CR_LVL_CB_BURST(x)		(x)
     82
     83#define SUN4I_TVE_TINT_BURST_PHASE_REG	0x110
     84#define SUN4I_TVE_TINT_BURST_PHASE_CHROMA(x)	(x)
     85
     86#define SUN4I_TVE_BURST_WIDTH_REG	0x114
     87#define SUN4I_TVE_BURST_WIDTH_BREEZEWAY(x)	((x) << 16)
     88#define SUN4I_TVE_BURST_WIDTH_BURST_WIDTH(x)	((x) << 8)
     89#define SUN4I_TVE_BURST_WIDTH_HSYNC_WIDTH(x)	(x)
     90
     91#define SUN4I_TVE_CB_CR_GAIN_REG	0x118
     92#define SUN4I_TVE_CB_CR_GAIN_CR(x)		((x) << 8)
     93#define SUN4I_TVE_CB_CR_GAIN_CB(x)		(x)
     94
     95#define SUN4I_TVE_SYNC_VBI_REG		0x11c
     96#define SUN4I_TVE_SYNC_VBI_SYNC(x)		((x) << 16)
     97#define SUN4I_TVE_SYNC_VBI_VBLANK(x)		(x)
     98
     99#define SUN4I_TVE_ACTIVE_LINE_REG	0x124
    100#define SUN4I_TVE_ACTIVE_LINE(x)		(x)
    101
    102#define SUN4I_TVE_CHROMA_REG		0x128
    103#define SUN4I_TVE_CHROMA_COMP_GAIN(x)		((x) & 3)
    104#define SUN4I_TVE_CHROMA_COMP_GAIN_50		SUN4I_TVE_CHROMA_COMP_GAIN(2)
    105
    106#define SUN4I_TVE_12C_REG		0x12c
    107#define SUN4I_TVE_12C_NOTCH_WIDTH_WIDE		BIT(8)
    108#define SUN4I_TVE_12C_COMP_YUV_EN		BIT(0)
    109
    110#define SUN4I_TVE_RESYNC_REG		0x130
    111#define SUN4I_TVE_RESYNC_FIELD			BIT(31)
    112#define SUN4I_TVE_RESYNC_LINE(x)		((x) << 16)
    113#define SUN4I_TVE_RESYNC_PIXEL(x)		(x)
    114
    115#define SUN4I_TVE_SLAVE_REG		0x134
    116
    117#define SUN4I_TVE_WSS_DATA2_REG		0x244
    118
    119struct color_gains {
    120	u16	cb;
    121	u16	cr;
    122};
    123
    124struct burst_levels {
    125	u16	cb;
    126	u16	cr;
    127};
    128
    129struct video_levels {
    130	u16	black;
    131	u16	blank;
    132};
    133
    134struct resync_parameters {
    135	bool	field;
    136	u16	line;
    137	u16	pixel;
    138};
    139
    140struct tv_mode {
    141	char		*name;
    142
    143	u32		mode;
    144	u32		chroma_freq;
    145	u16		back_porch;
    146	u16		front_porch;
    147	u16		line_number;
    148	u16		vblank_level;
    149
    150	u32		hdisplay;
    151	u16		hfront_porch;
    152	u16		hsync_len;
    153	u16		hback_porch;
    154
    155	u32		vdisplay;
    156	u16		vfront_porch;
    157	u16		vsync_len;
    158	u16		vback_porch;
    159
    160	bool		yc_en;
    161	bool		dac3_en;
    162	bool		dac_bit25_en;
    163
    164	const struct color_gains	*color_gains;
    165	const struct burst_levels	*burst_levels;
    166	const struct video_levels	*video_levels;
    167	const struct resync_parameters	*resync_params;
    168};
    169
    170struct sun4i_tv {
    171	struct drm_connector	connector;
    172	struct drm_encoder	encoder;
    173
    174	struct clk		*clk;
    175	struct regmap		*regs;
    176	struct reset_control	*reset;
    177
    178	struct sun4i_drv	*drv;
    179};
    180
    181static const struct video_levels ntsc_video_levels = {
    182	.black = 282,	.blank = 240,
    183};
    184
    185static const struct video_levels pal_video_levels = {
    186	.black = 252,	.blank = 252,
    187};
    188
    189static const struct burst_levels ntsc_burst_levels = {
    190	.cb = 79,	.cr = 0,
    191};
    192
    193static const struct burst_levels pal_burst_levels = {
    194	.cb = 40,	.cr = 40,
    195};
    196
    197static const struct color_gains ntsc_color_gains = {
    198	.cb = 160,	.cr = 160,
    199};
    200
    201static const struct color_gains pal_color_gains = {
    202	.cb = 224,	.cr = 224,
    203};
    204
    205static const struct resync_parameters ntsc_resync_parameters = {
    206	.field = false,	.line = 14,	.pixel = 12,
    207};
    208
    209static const struct resync_parameters pal_resync_parameters = {
    210	.field = true,	.line = 13,	.pixel = 12,
    211};
    212
    213static const struct tv_mode tv_modes[] = {
    214	{
    215		.name		= "NTSC",
    216		.mode		= SUN4I_TVE_CFG0_RES_480i,
    217		.chroma_freq	= 0x21f07c1f,
    218		.yc_en		= true,
    219		.dac3_en	= true,
    220		.dac_bit25_en	= true,
    221
    222		.back_porch	= 118,
    223		.front_porch	= 32,
    224		.line_number	= 525,
    225
    226		.hdisplay	= 720,
    227		.hfront_porch	= 18,
    228		.hsync_len	= 2,
    229		.hback_porch	= 118,
    230
    231		.vdisplay	= 480,
    232		.vfront_porch	= 26,
    233		.vsync_len	= 2,
    234		.vback_porch	= 17,
    235
    236		.vblank_level	= 240,
    237
    238		.color_gains	= &ntsc_color_gains,
    239		.burst_levels	= &ntsc_burst_levels,
    240		.video_levels	= &ntsc_video_levels,
    241		.resync_params	= &ntsc_resync_parameters,
    242	},
    243	{
    244		.name		= "PAL",
    245		.mode		= SUN4I_TVE_CFG0_RES_576i,
    246		.chroma_freq	= 0x2a098acb,
    247
    248		.back_porch	= 138,
    249		.front_porch	= 24,
    250		.line_number	= 625,
    251
    252		.hdisplay	= 720,
    253		.hfront_porch	= 3,
    254		.hsync_len	= 2,
    255		.hback_porch	= 139,
    256
    257		.vdisplay	= 576,
    258		.vfront_porch	= 28,
    259		.vsync_len	= 2,
    260		.vback_porch	= 19,
    261
    262		.vblank_level	= 252,
    263
    264		.color_gains	= &pal_color_gains,
    265		.burst_levels	= &pal_burst_levels,
    266		.video_levels	= &pal_video_levels,
    267		.resync_params	= &pal_resync_parameters,
    268	},
    269};
    270
    271static inline struct sun4i_tv *
    272drm_encoder_to_sun4i_tv(struct drm_encoder *encoder)
    273{
    274	return container_of(encoder, struct sun4i_tv,
    275			    encoder);
    276}
    277
    278static inline struct sun4i_tv *
    279drm_connector_to_sun4i_tv(struct drm_connector *connector)
    280{
    281	return container_of(connector, struct sun4i_tv,
    282			    connector);
    283}
    284
    285/*
    286 * FIXME: If only the drm_display_mode private field was usable, this
    287 * could go away...
    288 *
    289 * So far, it doesn't seem to be preserved when the mode is passed by
    290 * to mode_set for some reason.
    291 */
    292static const struct tv_mode *sun4i_tv_find_tv_by_mode(const struct drm_display_mode *mode)
    293{
    294	int i;
    295
    296	/* First try to identify the mode by name */
    297	for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
    298		const struct tv_mode *tv_mode = &tv_modes[i];
    299
    300		DRM_DEBUG_DRIVER("Comparing mode %s vs %s",
    301				 mode->name, tv_mode->name);
    302
    303		if (!strcmp(mode->name, tv_mode->name))
    304			return tv_mode;
    305	}
    306
    307	/* Then by number of lines */
    308	for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
    309		const struct tv_mode *tv_mode = &tv_modes[i];
    310
    311		DRM_DEBUG_DRIVER("Comparing mode %s vs %s (X: %d vs %d)",
    312				 mode->name, tv_mode->name,
    313				 mode->vdisplay, tv_mode->vdisplay);
    314
    315		if (mode->vdisplay == tv_mode->vdisplay)
    316			return tv_mode;
    317	}
    318
    319	return NULL;
    320}
    321
    322static void sun4i_tv_mode_to_drm_mode(const struct tv_mode *tv_mode,
    323				      struct drm_display_mode *mode)
    324{
    325	DRM_DEBUG_DRIVER("Creating mode %s\n", mode->name);
    326
    327	mode->type = DRM_MODE_TYPE_DRIVER;
    328	mode->clock = 13500;
    329	mode->flags = DRM_MODE_FLAG_INTERLACE;
    330
    331	mode->hdisplay = tv_mode->hdisplay;
    332	mode->hsync_start = mode->hdisplay + tv_mode->hfront_porch;
    333	mode->hsync_end = mode->hsync_start + tv_mode->hsync_len;
    334	mode->htotal = mode->hsync_end  + tv_mode->hback_porch;
    335
    336	mode->vdisplay = tv_mode->vdisplay;
    337	mode->vsync_start = mode->vdisplay + tv_mode->vfront_porch;
    338	mode->vsync_end = mode->vsync_start + tv_mode->vsync_len;
    339	mode->vtotal = mode->vsync_end  + tv_mode->vback_porch;
    340}
    341
    342static void sun4i_tv_disable(struct drm_encoder *encoder)
    343{
    344	struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
    345	struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc);
    346
    347	DRM_DEBUG_DRIVER("Disabling the TV Output\n");
    348
    349	regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
    350			   SUN4I_TVE_EN_ENABLE,
    351			   0);
    352
    353	sunxi_engine_disable_color_correction(crtc->engine);
    354}
    355
    356static void sun4i_tv_enable(struct drm_encoder *encoder)
    357{
    358	struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
    359	struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc);
    360
    361	DRM_DEBUG_DRIVER("Enabling the TV Output\n");
    362
    363	sunxi_engine_apply_color_correction(crtc->engine);
    364
    365	regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
    366			   SUN4I_TVE_EN_ENABLE,
    367			   SUN4I_TVE_EN_ENABLE);
    368}
    369
    370static void sun4i_tv_mode_set(struct drm_encoder *encoder,
    371			      struct drm_display_mode *mode,
    372			      struct drm_display_mode *adjusted_mode)
    373{
    374	struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
    375	const struct tv_mode *tv_mode = sun4i_tv_find_tv_by_mode(mode);
    376
    377	/* Enable and map the DAC to the output */
    378	regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
    379			   SUN4I_TVE_EN_DAC_MAP_MASK,
    380			   SUN4I_TVE_EN_DAC_MAP(0, 1) |
    381			   SUN4I_TVE_EN_DAC_MAP(1, 2) |
    382			   SUN4I_TVE_EN_DAC_MAP(2, 3) |
    383			   SUN4I_TVE_EN_DAC_MAP(3, 4));
    384
    385	/* Set PAL settings */
    386	regmap_write(tv->regs, SUN4I_TVE_CFG0_REG,
    387		     tv_mode->mode |
    388		     (tv_mode->yc_en ? SUN4I_TVE_CFG0_YC_EN : 0) |
    389		     SUN4I_TVE_CFG0_COMP_EN |
    390		     SUN4I_TVE_CFG0_DAC_CONTROL_54M |
    391		     SUN4I_TVE_CFG0_CORE_DATAPATH_54M |
    392		     SUN4I_TVE_CFG0_CORE_CONTROL_54M);
    393
    394	/* Configure the DAC for a composite output */
    395	regmap_write(tv->regs, SUN4I_TVE_DAC0_REG,
    396		     SUN4I_TVE_DAC0_DAC_EN(0) |
    397		     (tv_mode->dac3_en ? SUN4I_TVE_DAC0_DAC_EN(3) : 0) |
    398		     SUN4I_TVE_DAC0_INTERNAL_DAC_37_5_OHMS |
    399		     SUN4I_TVE_DAC0_CHROMA_0_75 |
    400		     SUN4I_TVE_DAC0_LUMA_0_4 |
    401		     SUN4I_TVE_DAC0_CLOCK_INVERT |
    402		     (tv_mode->dac_bit25_en ? BIT(25) : 0) |
    403		     BIT(30));
    404
    405	/* Configure the sample delay between DAC0 and the other DAC */
    406	regmap_write(tv->regs, SUN4I_TVE_NOTCH_REG,
    407		     SUN4I_TVE_NOTCH_DAC0_TO_DAC_DLY(1, 0) |
    408		     SUN4I_TVE_NOTCH_DAC0_TO_DAC_DLY(2, 0));
    409
    410	regmap_write(tv->regs, SUN4I_TVE_CHROMA_FREQ_REG,
    411		     tv_mode->chroma_freq);
    412
    413	/* Set the front and back porch */
    414	regmap_write(tv->regs, SUN4I_TVE_PORCH_REG,
    415		     SUN4I_TVE_PORCH_BACK(tv_mode->back_porch) |
    416		     SUN4I_TVE_PORCH_FRONT(tv_mode->front_porch));
    417
    418	/* Set the lines setup */
    419	regmap_write(tv->regs, SUN4I_TVE_LINE_REG,
    420		     SUN4I_TVE_LINE_FIRST(22) |
    421		     SUN4I_TVE_LINE_NUMBER(tv_mode->line_number));
    422
    423	regmap_write(tv->regs, SUN4I_TVE_LEVEL_REG,
    424		     SUN4I_TVE_LEVEL_BLANK(tv_mode->video_levels->blank) |
    425		     SUN4I_TVE_LEVEL_BLACK(tv_mode->video_levels->black));
    426
    427	regmap_write(tv->regs, SUN4I_TVE_DAC1_REG,
    428		     SUN4I_TVE_DAC1_AMPLITUDE(0, 0x18) |
    429		     SUN4I_TVE_DAC1_AMPLITUDE(1, 0x18) |
    430		     SUN4I_TVE_DAC1_AMPLITUDE(2, 0x18) |
    431		     SUN4I_TVE_DAC1_AMPLITUDE(3, 0x18));
    432
    433	regmap_write(tv->regs, SUN4I_TVE_CB_CR_LVL_REG,
    434		     SUN4I_TVE_CB_CR_LVL_CB_BURST(tv_mode->burst_levels->cb) |
    435		     SUN4I_TVE_CB_CR_LVL_CR_BURST(tv_mode->burst_levels->cr));
    436
    437	/* Set burst width for a composite output */
    438	regmap_write(tv->regs, SUN4I_TVE_BURST_WIDTH_REG,
    439		     SUN4I_TVE_BURST_WIDTH_HSYNC_WIDTH(126) |
    440		     SUN4I_TVE_BURST_WIDTH_BURST_WIDTH(68) |
    441		     SUN4I_TVE_BURST_WIDTH_BREEZEWAY(22));
    442
    443	regmap_write(tv->regs, SUN4I_TVE_CB_CR_GAIN_REG,
    444		     SUN4I_TVE_CB_CR_GAIN_CB(tv_mode->color_gains->cb) |
    445		     SUN4I_TVE_CB_CR_GAIN_CR(tv_mode->color_gains->cr));
    446
    447	regmap_write(tv->regs, SUN4I_TVE_SYNC_VBI_REG,
    448		     SUN4I_TVE_SYNC_VBI_SYNC(0x10) |
    449		     SUN4I_TVE_SYNC_VBI_VBLANK(tv_mode->vblank_level));
    450
    451	regmap_write(tv->regs, SUN4I_TVE_ACTIVE_LINE_REG,
    452		     SUN4I_TVE_ACTIVE_LINE(1440));
    453
    454	/* Set composite chroma gain to 50 % */
    455	regmap_write(tv->regs, SUN4I_TVE_CHROMA_REG,
    456		     SUN4I_TVE_CHROMA_COMP_GAIN_50);
    457
    458	regmap_write(tv->regs, SUN4I_TVE_12C_REG,
    459		     SUN4I_TVE_12C_COMP_YUV_EN |
    460		     SUN4I_TVE_12C_NOTCH_WIDTH_WIDE);
    461
    462	regmap_write(tv->regs, SUN4I_TVE_RESYNC_REG,
    463		     SUN4I_TVE_RESYNC_PIXEL(tv_mode->resync_params->pixel) |
    464		     SUN4I_TVE_RESYNC_LINE(tv_mode->resync_params->line) |
    465		     (tv_mode->resync_params->field ?
    466		      SUN4I_TVE_RESYNC_FIELD : 0));
    467
    468	regmap_write(tv->regs, SUN4I_TVE_SLAVE_REG, 0);
    469}
    470
    471static const struct drm_encoder_helper_funcs sun4i_tv_helper_funcs = {
    472	.disable	= sun4i_tv_disable,
    473	.enable		= sun4i_tv_enable,
    474	.mode_set	= sun4i_tv_mode_set,
    475};
    476
    477static int sun4i_tv_comp_get_modes(struct drm_connector *connector)
    478{
    479	int i;
    480
    481	for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
    482		struct drm_display_mode *mode;
    483		const struct tv_mode *tv_mode = &tv_modes[i];
    484
    485		mode = drm_mode_create(connector->dev);
    486		if (!mode) {
    487			DRM_ERROR("Failed to create a new display mode\n");
    488			return 0;
    489		}
    490
    491		strcpy(mode->name, tv_mode->name);
    492
    493		sun4i_tv_mode_to_drm_mode(tv_mode, mode);
    494		drm_mode_probed_add(connector, mode);
    495	}
    496
    497	return i;
    498}
    499
    500static int sun4i_tv_comp_mode_valid(struct drm_connector *connector,
    501				    struct drm_display_mode *mode)
    502{
    503	/* TODO */
    504	return MODE_OK;
    505}
    506
    507static const struct drm_connector_helper_funcs sun4i_tv_comp_connector_helper_funcs = {
    508	.get_modes	= sun4i_tv_comp_get_modes,
    509	.mode_valid	= sun4i_tv_comp_mode_valid,
    510};
    511
    512static void
    513sun4i_tv_comp_connector_destroy(struct drm_connector *connector)
    514{
    515	drm_connector_cleanup(connector);
    516}
    517
    518static const struct drm_connector_funcs sun4i_tv_comp_connector_funcs = {
    519	.fill_modes		= drm_helper_probe_single_connector_modes,
    520	.destroy		= sun4i_tv_comp_connector_destroy,
    521	.reset			= drm_atomic_helper_connector_reset,
    522	.atomic_duplicate_state	= drm_atomic_helper_connector_duplicate_state,
    523	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
    524};
    525
    526static const struct regmap_config sun4i_tv_regmap_config = {
    527	.reg_bits	= 32,
    528	.val_bits	= 32,
    529	.reg_stride	= 4,
    530	.max_register	= SUN4I_TVE_WSS_DATA2_REG,
    531	.name		= "tv-encoder",
    532};
    533
    534static int sun4i_tv_bind(struct device *dev, struct device *master,
    535			 void *data)
    536{
    537	struct platform_device *pdev = to_platform_device(dev);
    538	struct drm_device *drm = data;
    539	struct sun4i_drv *drv = drm->dev_private;
    540	struct sun4i_tv *tv;
    541	void __iomem *regs;
    542	int ret;
    543
    544	tv = devm_kzalloc(dev, sizeof(*tv), GFP_KERNEL);
    545	if (!tv)
    546		return -ENOMEM;
    547	tv->drv = drv;
    548	dev_set_drvdata(dev, tv);
    549
    550	regs = devm_platform_ioremap_resource(pdev, 0);
    551	if (IS_ERR(regs)) {
    552		dev_err(dev, "Couldn't map the TV encoder registers\n");
    553		return PTR_ERR(regs);
    554	}
    555
    556	tv->regs = devm_regmap_init_mmio(dev, regs,
    557					 &sun4i_tv_regmap_config);
    558	if (IS_ERR(tv->regs)) {
    559		dev_err(dev, "Couldn't create the TV encoder regmap\n");
    560		return PTR_ERR(tv->regs);
    561	}
    562
    563	tv->reset = devm_reset_control_get(dev, NULL);
    564	if (IS_ERR(tv->reset)) {
    565		dev_err(dev, "Couldn't get our reset line\n");
    566		return PTR_ERR(tv->reset);
    567	}
    568
    569	ret = reset_control_deassert(tv->reset);
    570	if (ret) {
    571		dev_err(dev, "Couldn't deassert our reset line\n");
    572		return ret;
    573	}
    574
    575	tv->clk = devm_clk_get(dev, NULL);
    576	if (IS_ERR(tv->clk)) {
    577		dev_err(dev, "Couldn't get the TV encoder clock\n");
    578		ret = PTR_ERR(tv->clk);
    579		goto err_assert_reset;
    580	}
    581	clk_prepare_enable(tv->clk);
    582
    583	drm_encoder_helper_add(&tv->encoder,
    584			       &sun4i_tv_helper_funcs);
    585	ret = drm_simple_encoder_init(drm, &tv->encoder,
    586				      DRM_MODE_ENCODER_TVDAC);
    587	if (ret) {
    588		dev_err(dev, "Couldn't initialise the TV encoder\n");
    589		goto err_disable_clk;
    590	}
    591
    592	tv->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm,
    593								dev->of_node);
    594	if (!tv->encoder.possible_crtcs) {
    595		ret = -EPROBE_DEFER;
    596		goto err_disable_clk;
    597	}
    598
    599	drm_connector_helper_add(&tv->connector,
    600				 &sun4i_tv_comp_connector_helper_funcs);
    601	ret = drm_connector_init(drm, &tv->connector,
    602				 &sun4i_tv_comp_connector_funcs,
    603				 DRM_MODE_CONNECTOR_Composite);
    604	if (ret) {
    605		dev_err(dev,
    606			"Couldn't initialise the Composite connector\n");
    607		goto err_cleanup_connector;
    608	}
    609	tv->connector.interlace_allowed = true;
    610
    611	drm_connector_attach_encoder(&tv->connector, &tv->encoder);
    612
    613	return 0;
    614
    615err_cleanup_connector:
    616	drm_encoder_cleanup(&tv->encoder);
    617err_disable_clk:
    618	clk_disable_unprepare(tv->clk);
    619err_assert_reset:
    620	reset_control_assert(tv->reset);
    621	return ret;
    622}
    623
    624static void sun4i_tv_unbind(struct device *dev, struct device *master,
    625			    void *data)
    626{
    627	struct sun4i_tv *tv = dev_get_drvdata(dev);
    628
    629	drm_connector_cleanup(&tv->connector);
    630	drm_encoder_cleanup(&tv->encoder);
    631	clk_disable_unprepare(tv->clk);
    632}
    633
    634static const struct component_ops sun4i_tv_ops = {
    635	.bind	= sun4i_tv_bind,
    636	.unbind	= sun4i_tv_unbind,
    637};
    638
    639static int sun4i_tv_probe(struct platform_device *pdev)
    640{
    641	return component_add(&pdev->dev, &sun4i_tv_ops);
    642}
    643
    644static int sun4i_tv_remove(struct platform_device *pdev)
    645{
    646	component_del(&pdev->dev, &sun4i_tv_ops);
    647
    648	return 0;
    649}
    650
    651static const struct of_device_id sun4i_tv_of_table[] = {
    652	{ .compatible = "allwinner,sun4i-a10-tv-encoder" },
    653	{ }
    654};
    655MODULE_DEVICE_TABLE(of, sun4i_tv_of_table);
    656
    657static struct platform_driver sun4i_tv_platform_driver = {
    658	.probe		= sun4i_tv_probe,
    659	.remove		= sun4i_tv_remove,
    660	.driver		= {
    661		.name		= "sun4i-tve",
    662		.of_match_table	= sun4i_tv_of_table,
    663	},
    664};
    665module_platform_driver(sun4i_tv_platform_driver);
    666
    667MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
    668MODULE_DESCRIPTION("Allwinner A10 TV Encoder Driver");
    669MODULE_LICENSE("GPL");