162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2015 Free Electrons 462306a36Sopenharmony_ci * Copyright (C) 2015 NextThing Co 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Maxime Ripard <maxime.ripard@free-electrons.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/clk.h> 1062306a36Sopenharmony_ci#include <linux/component.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/of_address.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/regmap.h> 1562306a36Sopenharmony_ci#include <linux/reset.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <drm/drm_atomic.h> 1862306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 1962306a36Sopenharmony_ci#include <drm/drm_of.h> 2062306a36Sopenharmony_ci#include <drm/drm_panel.h> 2162306a36Sopenharmony_ci#include <drm/drm_print.h> 2262306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 2362306a36Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "sun4i_crtc.h" 2662306a36Sopenharmony_ci#include "sun4i_drv.h" 2762306a36Sopenharmony_ci#include "sunxi_engine.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define SUN4I_TVE_EN_REG 0x000 3062306a36Sopenharmony_ci#define SUN4I_TVE_EN_DAC_MAP_MASK GENMASK(19, 4) 3162306a36Sopenharmony_ci#define SUN4I_TVE_EN_DAC_MAP(dac, out) (((out) & 0xf) << (dac + 1) * 4) 3262306a36Sopenharmony_ci#define SUN4I_TVE_EN_ENABLE BIT(0) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define SUN4I_TVE_CFG0_REG 0x004 3562306a36Sopenharmony_ci#define SUN4I_TVE_CFG0_DAC_CONTROL_54M BIT(26) 3662306a36Sopenharmony_ci#define SUN4I_TVE_CFG0_CORE_DATAPATH_54M BIT(25) 3762306a36Sopenharmony_ci#define SUN4I_TVE_CFG0_CORE_CONTROL_54M BIT(24) 3862306a36Sopenharmony_ci#define SUN4I_TVE_CFG0_YC_EN BIT(17) 3962306a36Sopenharmony_ci#define SUN4I_TVE_CFG0_COMP_EN BIT(16) 4062306a36Sopenharmony_ci#define SUN4I_TVE_CFG0_RES(x) ((x) & 0xf) 4162306a36Sopenharmony_ci#define SUN4I_TVE_CFG0_RES_480i SUN4I_TVE_CFG0_RES(0) 4262306a36Sopenharmony_ci#define SUN4I_TVE_CFG0_RES_576i SUN4I_TVE_CFG0_RES(1) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define SUN4I_TVE_DAC0_REG 0x008 4562306a36Sopenharmony_ci#define SUN4I_TVE_DAC0_CLOCK_INVERT BIT(24) 4662306a36Sopenharmony_ci#define SUN4I_TVE_DAC0_LUMA(x) (((x) & 3) << 20) 4762306a36Sopenharmony_ci#define SUN4I_TVE_DAC0_LUMA_0_4 SUN4I_TVE_DAC0_LUMA(3) 4862306a36Sopenharmony_ci#define SUN4I_TVE_DAC0_CHROMA(x) (((x) & 3) << 18) 4962306a36Sopenharmony_ci#define SUN4I_TVE_DAC0_CHROMA_0_75 SUN4I_TVE_DAC0_CHROMA(3) 5062306a36Sopenharmony_ci#define SUN4I_TVE_DAC0_INTERNAL_DAC(x) (((x) & 3) << 16) 5162306a36Sopenharmony_ci#define SUN4I_TVE_DAC0_INTERNAL_DAC_37_5_OHMS SUN4I_TVE_DAC0_INTERNAL_DAC(3) 5262306a36Sopenharmony_ci#define SUN4I_TVE_DAC0_DAC_EN(dac) BIT(dac) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define SUN4I_TVE_NOTCH_REG 0x00c 5562306a36Sopenharmony_ci#define SUN4I_TVE_NOTCH_DAC0_TO_DAC_DLY(dac, x) ((4 - (x)) << (dac * 3)) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define SUN4I_TVE_CHROMA_FREQ_REG 0x010 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define SUN4I_TVE_PORCH_REG 0x014 6062306a36Sopenharmony_ci#define SUN4I_TVE_PORCH_BACK(x) ((x) << 16) 6162306a36Sopenharmony_ci#define SUN4I_TVE_PORCH_FRONT(x) (x) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define SUN4I_TVE_LINE_REG 0x01c 6462306a36Sopenharmony_ci#define SUN4I_TVE_LINE_FIRST(x) ((x) << 16) 6562306a36Sopenharmony_ci#define SUN4I_TVE_LINE_NUMBER(x) (x) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define SUN4I_TVE_LEVEL_REG 0x020 6862306a36Sopenharmony_ci#define SUN4I_TVE_LEVEL_BLANK(x) ((x) << 16) 6962306a36Sopenharmony_ci#define SUN4I_TVE_LEVEL_BLACK(x) (x) 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define SUN4I_TVE_DAC1_REG 0x024 7262306a36Sopenharmony_ci#define SUN4I_TVE_DAC1_AMPLITUDE(dac, x) ((x) << (dac * 8)) 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define SUN4I_TVE_DETECT_STA_REG 0x038 7562306a36Sopenharmony_ci#define SUN4I_TVE_DETECT_STA_DAC(dac) BIT((dac * 8)) 7662306a36Sopenharmony_ci#define SUN4I_TVE_DETECT_STA_UNCONNECTED 0 7762306a36Sopenharmony_ci#define SUN4I_TVE_DETECT_STA_CONNECTED 1 7862306a36Sopenharmony_ci#define SUN4I_TVE_DETECT_STA_GROUND 2 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define SUN4I_TVE_CB_CR_LVL_REG 0x10c 8162306a36Sopenharmony_ci#define SUN4I_TVE_CB_CR_LVL_CR_BURST(x) ((x) << 8) 8262306a36Sopenharmony_ci#define SUN4I_TVE_CB_CR_LVL_CB_BURST(x) (x) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define SUN4I_TVE_TINT_BURST_PHASE_REG 0x110 8562306a36Sopenharmony_ci#define SUN4I_TVE_TINT_BURST_PHASE_CHROMA(x) (x) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define SUN4I_TVE_BURST_WIDTH_REG 0x114 8862306a36Sopenharmony_ci#define SUN4I_TVE_BURST_WIDTH_BREEZEWAY(x) ((x) << 16) 8962306a36Sopenharmony_ci#define SUN4I_TVE_BURST_WIDTH_BURST_WIDTH(x) ((x) << 8) 9062306a36Sopenharmony_ci#define SUN4I_TVE_BURST_WIDTH_HSYNC_WIDTH(x) (x) 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define SUN4I_TVE_CB_CR_GAIN_REG 0x118 9362306a36Sopenharmony_ci#define SUN4I_TVE_CB_CR_GAIN_CR(x) ((x) << 8) 9462306a36Sopenharmony_ci#define SUN4I_TVE_CB_CR_GAIN_CB(x) (x) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define SUN4I_TVE_SYNC_VBI_REG 0x11c 9762306a36Sopenharmony_ci#define SUN4I_TVE_SYNC_VBI_SYNC(x) ((x) << 16) 9862306a36Sopenharmony_ci#define SUN4I_TVE_SYNC_VBI_VBLANK(x) (x) 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define SUN4I_TVE_ACTIVE_LINE_REG 0x124 10162306a36Sopenharmony_ci#define SUN4I_TVE_ACTIVE_LINE(x) (x) 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define SUN4I_TVE_CHROMA_REG 0x128 10462306a36Sopenharmony_ci#define SUN4I_TVE_CHROMA_COMP_GAIN(x) ((x) & 3) 10562306a36Sopenharmony_ci#define SUN4I_TVE_CHROMA_COMP_GAIN_50 SUN4I_TVE_CHROMA_COMP_GAIN(2) 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci#define SUN4I_TVE_12C_REG 0x12c 10862306a36Sopenharmony_ci#define SUN4I_TVE_12C_NOTCH_WIDTH_WIDE BIT(8) 10962306a36Sopenharmony_ci#define SUN4I_TVE_12C_COMP_YUV_EN BIT(0) 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci#define SUN4I_TVE_RESYNC_REG 0x130 11262306a36Sopenharmony_ci#define SUN4I_TVE_RESYNC_FIELD BIT(31) 11362306a36Sopenharmony_ci#define SUN4I_TVE_RESYNC_LINE(x) ((x) << 16) 11462306a36Sopenharmony_ci#define SUN4I_TVE_RESYNC_PIXEL(x) (x) 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#define SUN4I_TVE_SLAVE_REG 0x134 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#define SUN4I_TVE_WSS_DATA2_REG 0x244 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistruct color_gains { 12162306a36Sopenharmony_ci u16 cb; 12262306a36Sopenharmony_ci u16 cr; 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistruct burst_levels { 12662306a36Sopenharmony_ci u16 cb; 12762306a36Sopenharmony_ci u16 cr; 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistruct video_levels { 13162306a36Sopenharmony_ci u16 black; 13262306a36Sopenharmony_ci u16 blank; 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistruct resync_parameters { 13662306a36Sopenharmony_ci bool field; 13762306a36Sopenharmony_ci u16 line; 13862306a36Sopenharmony_ci u16 pixel; 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistruct tv_mode { 14262306a36Sopenharmony_ci char *name; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci unsigned int tv_mode; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci u32 mode; 14762306a36Sopenharmony_ci u32 chroma_freq; 14862306a36Sopenharmony_ci u16 back_porch; 14962306a36Sopenharmony_ci u16 front_porch; 15062306a36Sopenharmony_ci u16 vblank_level; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci bool yc_en; 15362306a36Sopenharmony_ci bool dac3_en; 15462306a36Sopenharmony_ci bool dac_bit25_en; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci const struct color_gains *color_gains; 15762306a36Sopenharmony_ci const struct burst_levels *burst_levels; 15862306a36Sopenharmony_ci const struct video_levels *video_levels; 15962306a36Sopenharmony_ci const struct resync_parameters *resync_params; 16062306a36Sopenharmony_ci}; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistruct sun4i_tv { 16362306a36Sopenharmony_ci struct drm_connector connector; 16462306a36Sopenharmony_ci struct drm_encoder encoder; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci struct clk *clk; 16762306a36Sopenharmony_ci struct regmap *regs; 16862306a36Sopenharmony_ci struct reset_control *reset; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci struct sun4i_drv *drv; 17162306a36Sopenharmony_ci}; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic const struct video_levels ntsc_video_levels = { 17462306a36Sopenharmony_ci .black = 282, .blank = 240, 17562306a36Sopenharmony_ci}; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic const struct video_levels pal_video_levels = { 17862306a36Sopenharmony_ci .black = 252, .blank = 252, 17962306a36Sopenharmony_ci}; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic const struct burst_levels ntsc_burst_levels = { 18262306a36Sopenharmony_ci .cb = 79, .cr = 0, 18362306a36Sopenharmony_ci}; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic const struct burst_levels pal_burst_levels = { 18662306a36Sopenharmony_ci .cb = 40, .cr = 40, 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic const struct color_gains ntsc_color_gains = { 19062306a36Sopenharmony_ci .cb = 160, .cr = 160, 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic const struct color_gains pal_color_gains = { 19462306a36Sopenharmony_ci .cb = 224, .cr = 224, 19562306a36Sopenharmony_ci}; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic const struct resync_parameters ntsc_resync_parameters = { 19862306a36Sopenharmony_ci .field = false, .line = 14, .pixel = 12, 19962306a36Sopenharmony_ci}; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic const struct resync_parameters pal_resync_parameters = { 20262306a36Sopenharmony_ci .field = true, .line = 13, .pixel = 12, 20362306a36Sopenharmony_ci}; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic const struct tv_mode tv_modes[] = { 20662306a36Sopenharmony_ci { 20762306a36Sopenharmony_ci .tv_mode = DRM_MODE_TV_MODE_NTSC, 20862306a36Sopenharmony_ci .mode = SUN4I_TVE_CFG0_RES_480i, 20962306a36Sopenharmony_ci .chroma_freq = 0x21f07c1f, 21062306a36Sopenharmony_ci .yc_en = true, 21162306a36Sopenharmony_ci .dac3_en = true, 21262306a36Sopenharmony_ci .dac_bit25_en = true, 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci .back_porch = 118, 21562306a36Sopenharmony_ci .front_porch = 32, 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci .vblank_level = 240, 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci .color_gains = &ntsc_color_gains, 22062306a36Sopenharmony_ci .burst_levels = &ntsc_burst_levels, 22162306a36Sopenharmony_ci .video_levels = &ntsc_video_levels, 22262306a36Sopenharmony_ci .resync_params = &ntsc_resync_parameters, 22362306a36Sopenharmony_ci }, 22462306a36Sopenharmony_ci { 22562306a36Sopenharmony_ci .tv_mode = DRM_MODE_TV_MODE_PAL, 22662306a36Sopenharmony_ci .mode = SUN4I_TVE_CFG0_RES_576i, 22762306a36Sopenharmony_ci .chroma_freq = 0x2a098acb, 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci .back_porch = 138, 23062306a36Sopenharmony_ci .front_porch = 24, 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci .vblank_level = 252, 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci .color_gains = &pal_color_gains, 23562306a36Sopenharmony_ci .burst_levels = &pal_burst_levels, 23662306a36Sopenharmony_ci .video_levels = &pal_video_levels, 23762306a36Sopenharmony_ci .resync_params = &pal_resync_parameters, 23862306a36Sopenharmony_ci }, 23962306a36Sopenharmony_ci}; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic inline struct sun4i_tv * 24262306a36Sopenharmony_cidrm_encoder_to_sun4i_tv(struct drm_encoder *encoder) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci return container_of(encoder, struct sun4i_tv, 24562306a36Sopenharmony_ci encoder); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic const struct tv_mode * 24962306a36Sopenharmony_cisun4i_tv_find_tv_by_mode(unsigned int mode) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci int i; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tv_modes); i++) { 25462306a36Sopenharmony_ci const struct tv_mode *tv_mode = &tv_modes[i]; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (tv_mode->tv_mode == mode) 25762306a36Sopenharmony_ci return tv_mode; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return NULL; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic void sun4i_tv_disable(struct drm_encoder *encoder, 26462306a36Sopenharmony_ci struct drm_atomic_state *state) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder); 26762306a36Sopenharmony_ci struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Disabling the TV Output\n"); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG, 27262306a36Sopenharmony_ci SUN4I_TVE_EN_ENABLE, 27362306a36Sopenharmony_ci 0); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci sunxi_engine_disable_color_correction(crtc->engine); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic void sun4i_tv_enable(struct drm_encoder *encoder, 27962306a36Sopenharmony_ci struct drm_atomic_state *state) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder); 28262306a36Sopenharmony_ci struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc); 28362306a36Sopenharmony_ci struct drm_crtc_state *crtc_state = 28462306a36Sopenharmony_ci drm_atomic_get_new_crtc_state(state, encoder->crtc); 28562306a36Sopenharmony_ci struct drm_display_mode *mode = &crtc_state->mode; 28662306a36Sopenharmony_ci struct drm_connector *connector = &tv->connector; 28762306a36Sopenharmony_ci struct drm_connector_state *conn_state = 28862306a36Sopenharmony_ci drm_atomic_get_new_connector_state(state, connector); 28962306a36Sopenharmony_ci const struct tv_mode *tv_mode = 29062306a36Sopenharmony_ci sun4i_tv_find_tv_by_mode(conn_state->tv.mode); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Enabling the TV Output\n"); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* Enable and map the DAC to the output */ 29562306a36Sopenharmony_ci regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG, 29662306a36Sopenharmony_ci SUN4I_TVE_EN_DAC_MAP_MASK, 29762306a36Sopenharmony_ci SUN4I_TVE_EN_DAC_MAP(0, 1) | 29862306a36Sopenharmony_ci SUN4I_TVE_EN_DAC_MAP(1, 2) | 29962306a36Sopenharmony_ci SUN4I_TVE_EN_DAC_MAP(2, 3) | 30062306a36Sopenharmony_ci SUN4I_TVE_EN_DAC_MAP(3, 4)); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Set PAL settings */ 30362306a36Sopenharmony_ci regmap_write(tv->regs, SUN4I_TVE_CFG0_REG, 30462306a36Sopenharmony_ci tv_mode->mode | 30562306a36Sopenharmony_ci (tv_mode->yc_en ? SUN4I_TVE_CFG0_YC_EN : 0) | 30662306a36Sopenharmony_ci SUN4I_TVE_CFG0_COMP_EN | 30762306a36Sopenharmony_ci SUN4I_TVE_CFG0_DAC_CONTROL_54M | 30862306a36Sopenharmony_ci SUN4I_TVE_CFG0_CORE_DATAPATH_54M | 30962306a36Sopenharmony_ci SUN4I_TVE_CFG0_CORE_CONTROL_54M); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* Configure the DAC for a composite output */ 31262306a36Sopenharmony_ci regmap_write(tv->regs, SUN4I_TVE_DAC0_REG, 31362306a36Sopenharmony_ci SUN4I_TVE_DAC0_DAC_EN(0) | 31462306a36Sopenharmony_ci (tv_mode->dac3_en ? SUN4I_TVE_DAC0_DAC_EN(3) : 0) | 31562306a36Sopenharmony_ci SUN4I_TVE_DAC0_INTERNAL_DAC_37_5_OHMS | 31662306a36Sopenharmony_ci SUN4I_TVE_DAC0_CHROMA_0_75 | 31762306a36Sopenharmony_ci SUN4I_TVE_DAC0_LUMA_0_4 | 31862306a36Sopenharmony_ci SUN4I_TVE_DAC0_CLOCK_INVERT | 31962306a36Sopenharmony_ci (tv_mode->dac_bit25_en ? BIT(25) : 0) | 32062306a36Sopenharmony_ci BIT(30)); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* Configure the sample delay between DAC0 and the other DAC */ 32362306a36Sopenharmony_ci regmap_write(tv->regs, SUN4I_TVE_NOTCH_REG, 32462306a36Sopenharmony_ci SUN4I_TVE_NOTCH_DAC0_TO_DAC_DLY(1, 0) | 32562306a36Sopenharmony_ci SUN4I_TVE_NOTCH_DAC0_TO_DAC_DLY(2, 0)); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci regmap_write(tv->regs, SUN4I_TVE_CHROMA_FREQ_REG, 32862306a36Sopenharmony_ci tv_mode->chroma_freq); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* Set the front and back porch */ 33162306a36Sopenharmony_ci regmap_write(tv->regs, SUN4I_TVE_PORCH_REG, 33262306a36Sopenharmony_ci SUN4I_TVE_PORCH_BACK(tv_mode->back_porch) | 33362306a36Sopenharmony_ci SUN4I_TVE_PORCH_FRONT(tv_mode->front_porch)); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* Set the lines setup */ 33662306a36Sopenharmony_ci regmap_write(tv->regs, SUN4I_TVE_LINE_REG, 33762306a36Sopenharmony_ci SUN4I_TVE_LINE_FIRST(22) | 33862306a36Sopenharmony_ci SUN4I_TVE_LINE_NUMBER(mode->vtotal)); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci regmap_write(tv->regs, SUN4I_TVE_LEVEL_REG, 34162306a36Sopenharmony_ci SUN4I_TVE_LEVEL_BLANK(tv_mode->video_levels->blank) | 34262306a36Sopenharmony_ci SUN4I_TVE_LEVEL_BLACK(tv_mode->video_levels->black)); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci regmap_write(tv->regs, SUN4I_TVE_DAC1_REG, 34562306a36Sopenharmony_ci SUN4I_TVE_DAC1_AMPLITUDE(0, 0x18) | 34662306a36Sopenharmony_ci SUN4I_TVE_DAC1_AMPLITUDE(1, 0x18) | 34762306a36Sopenharmony_ci SUN4I_TVE_DAC1_AMPLITUDE(2, 0x18) | 34862306a36Sopenharmony_ci SUN4I_TVE_DAC1_AMPLITUDE(3, 0x18)); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci regmap_write(tv->regs, SUN4I_TVE_CB_CR_LVL_REG, 35162306a36Sopenharmony_ci SUN4I_TVE_CB_CR_LVL_CB_BURST(tv_mode->burst_levels->cb) | 35262306a36Sopenharmony_ci SUN4I_TVE_CB_CR_LVL_CR_BURST(tv_mode->burst_levels->cr)); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* Set burst width for a composite output */ 35562306a36Sopenharmony_ci regmap_write(tv->regs, SUN4I_TVE_BURST_WIDTH_REG, 35662306a36Sopenharmony_ci SUN4I_TVE_BURST_WIDTH_HSYNC_WIDTH(126) | 35762306a36Sopenharmony_ci SUN4I_TVE_BURST_WIDTH_BURST_WIDTH(68) | 35862306a36Sopenharmony_ci SUN4I_TVE_BURST_WIDTH_BREEZEWAY(22)); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci regmap_write(tv->regs, SUN4I_TVE_CB_CR_GAIN_REG, 36162306a36Sopenharmony_ci SUN4I_TVE_CB_CR_GAIN_CB(tv_mode->color_gains->cb) | 36262306a36Sopenharmony_ci SUN4I_TVE_CB_CR_GAIN_CR(tv_mode->color_gains->cr)); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci regmap_write(tv->regs, SUN4I_TVE_SYNC_VBI_REG, 36562306a36Sopenharmony_ci SUN4I_TVE_SYNC_VBI_SYNC(0x10) | 36662306a36Sopenharmony_ci SUN4I_TVE_SYNC_VBI_VBLANK(tv_mode->vblank_level)); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci regmap_write(tv->regs, SUN4I_TVE_ACTIVE_LINE_REG, 36962306a36Sopenharmony_ci SUN4I_TVE_ACTIVE_LINE(1440)); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* Set composite chroma gain to 50 % */ 37262306a36Sopenharmony_ci regmap_write(tv->regs, SUN4I_TVE_CHROMA_REG, 37362306a36Sopenharmony_ci SUN4I_TVE_CHROMA_COMP_GAIN_50); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci regmap_write(tv->regs, SUN4I_TVE_12C_REG, 37662306a36Sopenharmony_ci SUN4I_TVE_12C_COMP_YUV_EN | 37762306a36Sopenharmony_ci SUN4I_TVE_12C_NOTCH_WIDTH_WIDE); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci regmap_write(tv->regs, SUN4I_TVE_RESYNC_REG, 38062306a36Sopenharmony_ci SUN4I_TVE_RESYNC_PIXEL(tv_mode->resync_params->pixel) | 38162306a36Sopenharmony_ci SUN4I_TVE_RESYNC_LINE(tv_mode->resync_params->line) | 38262306a36Sopenharmony_ci (tv_mode->resync_params->field ? 38362306a36Sopenharmony_ci SUN4I_TVE_RESYNC_FIELD : 0)); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci regmap_write(tv->regs, SUN4I_TVE_SLAVE_REG, 0); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci sunxi_engine_apply_color_correction(crtc->engine); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG, 39062306a36Sopenharmony_ci SUN4I_TVE_EN_ENABLE, 39162306a36Sopenharmony_ci SUN4I_TVE_EN_ENABLE); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic const struct drm_encoder_helper_funcs sun4i_tv_helper_funcs = { 39562306a36Sopenharmony_ci .atomic_disable = sun4i_tv_disable, 39662306a36Sopenharmony_ci .atomic_enable = sun4i_tv_enable, 39762306a36Sopenharmony_ci}; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic const struct drm_connector_helper_funcs sun4i_tv_comp_connector_helper_funcs = { 40062306a36Sopenharmony_ci .atomic_check = drm_atomic_helper_connector_tv_check, 40162306a36Sopenharmony_ci .get_modes = drm_connector_helper_tv_get_modes, 40262306a36Sopenharmony_ci}; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic void sun4i_tv_connector_reset(struct drm_connector *connector) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci drm_atomic_helper_connector_reset(connector); 40762306a36Sopenharmony_ci drm_atomic_helper_connector_tv_reset(connector); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic const struct drm_connector_funcs sun4i_tv_comp_connector_funcs = { 41162306a36Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 41262306a36Sopenharmony_ci .destroy = drm_connector_cleanup, 41362306a36Sopenharmony_ci .reset = sun4i_tv_connector_reset, 41462306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 41562306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 41662306a36Sopenharmony_ci}; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic const struct regmap_config sun4i_tv_regmap_config = { 41962306a36Sopenharmony_ci .reg_bits = 32, 42062306a36Sopenharmony_ci .val_bits = 32, 42162306a36Sopenharmony_ci .reg_stride = 4, 42262306a36Sopenharmony_ci .max_register = SUN4I_TVE_WSS_DATA2_REG, 42362306a36Sopenharmony_ci .name = "tv-encoder", 42462306a36Sopenharmony_ci}; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int sun4i_tv_bind(struct device *dev, struct device *master, 42762306a36Sopenharmony_ci void *data) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 43062306a36Sopenharmony_ci struct drm_device *drm = data; 43162306a36Sopenharmony_ci struct sun4i_drv *drv = drm->dev_private; 43262306a36Sopenharmony_ci struct sun4i_tv *tv; 43362306a36Sopenharmony_ci void __iomem *regs; 43462306a36Sopenharmony_ci int ret; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci tv = devm_kzalloc(dev, sizeof(*tv), GFP_KERNEL); 43762306a36Sopenharmony_ci if (!tv) 43862306a36Sopenharmony_ci return -ENOMEM; 43962306a36Sopenharmony_ci tv->drv = drv; 44062306a36Sopenharmony_ci dev_set_drvdata(dev, tv); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci regs = devm_platform_ioremap_resource(pdev, 0); 44362306a36Sopenharmony_ci if (IS_ERR(regs)) { 44462306a36Sopenharmony_ci dev_err(dev, "Couldn't map the TV encoder registers\n"); 44562306a36Sopenharmony_ci return PTR_ERR(regs); 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci tv->regs = devm_regmap_init_mmio(dev, regs, 44962306a36Sopenharmony_ci &sun4i_tv_regmap_config); 45062306a36Sopenharmony_ci if (IS_ERR(tv->regs)) { 45162306a36Sopenharmony_ci dev_err(dev, "Couldn't create the TV encoder regmap\n"); 45262306a36Sopenharmony_ci return PTR_ERR(tv->regs); 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci tv->reset = devm_reset_control_get(dev, NULL); 45662306a36Sopenharmony_ci if (IS_ERR(tv->reset)) { 45762306a36Sopenharmony_ci dev_err(dev, "Couldn't get our reset line\n"); 45862306a36Sopenharmony_ci return PTR_ERR(tv->reset); 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci ret = reset_control_deassert(tv->reset); 46262306a36Sopenharmony_ci if (ret) { 46362306a36Sopenharmony_ci dev_err(dev, "Couldn't deassert our reset line\n"); 46462306a36Sopenharmony_ci return ret; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci tv->clk = devm_clk_get(dev, NULL); 46862306a36Sopenharmony_ci if (IS_ERR(tv->clk)) { 46962306a36Sopenharmony_ci dev_err(dev, "Couldn't get the TV encoder clock\n"); 47062306a36Sopenharmony_ci ret = PTR_ERR(tv->clk); 47162306a36Sopenharmony_ci goto err_assert_reset; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci clk_prepare_enable(tv->clk); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci drm_encoder_helper_add(&tv->encoder, 47662306a36Sopenharmony_ci &sun4i_tv_helper_funcs); 47762306a36Sopenharmony_ci ret = drm_simple_encoder_init(drm, &tv->encoder, 47862306a36Sopenharmony_ci DRM_MODE_ENCODER_TVDAC); 47962306a36Sopenharmony_ci if (ret) { 48062306a36Sopenharmony_ci dev_err(dev, "Couldn't initialise the TV encoder\n"); 48162306a36Sopenharmony_ci goto err_disable_clk; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci tv->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm, 48562306a36Sopenharmony_ci dev->of_node); 48662306a36Sopenharmony_ci if (!tv->encoder.possible_crtcs) { 48762306a36Sopenharmony_ci ret = -EPROBE_DEFER; 48862306a36Sopenharmony_ci goto err_disable_clk; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci drm_connector_helper_add(&tv->connector, 49262306a36Sopenharmony_ci &sun4i_tv_comp_connector_helper_funcs); 49362306a36Sopenharmony_ci ret = drm_connector_init(drm, &tv->connector, 49462306a36Sopenharmony_ci &sun4i_tv_comp_connector_funcs, 49562306a36Sopenharmony_ci DRM_MODE_CONNECTOR_Composite); 49662306a36Sopenharmony_ci if (ret) { 49762306a36Sopenharmony_ci dev_err(dev, 49862306a36Sopenharmony_ci "Couldn't initialise the Composite connector\n"); 49962306a36Sopenharmony_ci goto err_cleanup_encoder; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci tv->connector.interlace_allowed = true; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci drm_connector_attach_encoder(&tv->connector, &tv->encoder); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci ret = drm_mode_create_tv_properties(drm, 50662306a36Sopenharmony_ci BIT(DRM_MODE_TV_MODE_NTSC) | 50762306a36Sopenharmony_ci BIT(DRM_MODE_TV_MODE_PAL)); 50862306a36Sopenharmony_ci if (ret) 50962306a36Sopenharmony_ci goto err_cleanup_connector; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci drm_object_attach_property(&tv->connector.base, 51262306a36Sopenharmony_ci drm->mode_config.tv_mode_property, 51362306a36Sopenharmony_ci DRM_MODE_TV_MODE_NTSC); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return 0; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cierr_cleanup_connector: 51862306a36Sopenharmony_ci drm_connector_cleanup(&tv->connector); 51962306a36Sopenharmony_cierr_cleanup_encoder: 52062306a36Sopenharmony_ci drm_encoder_cleanup(&tv->encoder); 52162306a36Sopenharmony_cierr_disable_clk: 52262306a36Sopenharmony_ci clk_disable_unprepare(tv->clk); 52362306a36Sopenharmony_cierr_assert_reset: 52462306a36Sopenharmony_ci reset_control_assert(tv->reset); 52562306a36Sopenharmony_ci return ret; 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic void sun4i_tv_unbind(struct device *dev, struct device *master, 52962306a36Sopenharmony_ci void *data) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct sun4i_tv *tv = dev_get_drvdata(dev); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci drm_connector_cleanup(&tv->connector); 53462306a36Sopenharmony_ci drm_encoder_cleanup(&tv->encoder); 53562306a36Sopenharmony_ci clk_disable_unprepare(tv->clk); 53662306a36Sopenharmony_ci reset_control_assert(tv->reset); 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic const struct component_ops sun4i_tv_ops = { 54062306a36Sopenharmony_ci .bind = sun4i_tv_bind, 54162306a36Sopenharmony_ci .unbind = sun4i_tv_unbind, 54262306a36Sopenharmony_ci}; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic int sun4i_tv_probe(struct platform_device *pdev) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci return component_add(&pdev->dev, &sun4i_tv_ops); 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic void sun4i_tv_remove(struct platform_device *pdev) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci component_del(&pdev->dev, &sun4i_tv_ops); 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic const struct of_device_id sun4i_tv_of_table[] = { 55562306a36Sopenharmony_ci { .compatible = "allwinner,sun4i-a10-tv-encoder" }, 55662306a36Sopenharmony_ci { } 55762306a36Sopenharmony_ci}; 55862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sun4i_tv_of_table); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic struct platform_driver sun4i_tv_platform_driver = { 56162306a36Sopenharmony_ci .probe = sun4i_tv_probe, 56262306a36Sopenharmony_ci .remove_new = sun4i_tv_remove, 56362306a36Sopenharmony_ci .driver = { 56462306a36Sopenharmony_ci .name = "sun4i-tve", 56562306a36Sopenharmony_ci .of_match_table = sun4i_tv_of_table, 56662306a36Sopenharmony_ci }, 56762306a36Sopenharmony_ci}; 56862306a36Sopenharmony_cimodule_platform_driver(sun4i_tv_platform_driver); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ciMODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); 57162306a36Sopenharmony_ciMODULE_DESCRIPTION("Allwinner A10 TV Encoder Driver"); 57262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 573