1e2842570SBenjamin Gaignard // SPDX-License-Identifier: GPL-2.0 2cdfbff78SBenjamin Gaignard /* 3cdfbff78SBenjamin Gaignard * Copyright (C) STMicroelectronics SA 2014 4cdfbff78SBenjamin Gaignard * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> 5cdfbff78SBenjamin Gaignard * Vincent Abriou <vincent.abriou@st.com> 6cdfbff78SBenjamin Gaignard * for STMicroelectronics. 7cdfbff78SBenjamin Gaignard */ 8cdfbff78SBenjamin Gaignard 9cdfbff78SBenjamin Gaignard #include <linux/clk.h> 10cdfbff78SBenjamin Gaignard #include <linux/component.h> 11cdfbff78SBenjamin Gaignard #include <linux/module.h> 12cdfbff78SBenjamin Gaignard #include <linux/of_platform.h> 13cdfbff78SBenjamin Gaignard #include <linux/platform_device.h> 14cdfbff78SBenjamin Gaignard #include <linux/reset.h> 150f3e1561SArnd Bergmann #include <linux/seq_file.h> 16cdfbff78SBenjamin Gaignard 17cdfbff78SBenjamin Gaignard #include <drm/drmP.h> 18*fcd70cd3SDaniel Vetter #include <drm/drm_atomic_helper.h> 19cdfbff78SBenjamin Gaignard 209e1f05b2SVincent Abriou #include "sti_crtc.h" 21bdfd36efSVille Syrjälä #include "sti_drv.h" 22503290ceSVincent Abriou #include "sti_vtg.h" 235e03abc5SBenjamin Gaignard 24cdfbff78SBenjamin Gaignard /* glue registers */ 25cdfbff78SBenjamin Gaignard #define TVO_CSC_MAIN_M0 0x000 26cdfbff78SBenjamin Gaignard #define TVO_CSC_MAIN_M1 0x004 27cdfbff78SBenjamin Gaignard #define TVO_CSC_MAIN_M2 0x008 28cdfbff78SBenjamin Gaignard #define TVO_CSC_MAIN_M3 0x00c 29cdfbff78SBenjamin Gaignard #define TVO_CSC_MAIN_M4 0x010 30cdfbff78SBenjamin Gaignard #define TVO_CSC_MAIN_M5 0x014 31cdfbff78SBenjamin Gaignard #define TVO_CSC_MAIN_M6 0x018 32cdfbff78SBenjamin Gaignard #define TVO_CSC_MAIN_M7 0x01c 33cdfbff78SBenjamin Gaignard #define TVO_MAIN_IN_VID_FORMAT 0x030 34cdfbff78SBenjamin Gaignard #define TVO_CSC_AUX_M0 0x100 35cdfbff78SBenjamin Gaignard #define TVO_CSC_AUX_M1 0x104 36cdfbff78SBenjamin Gaignard #define TVO_CSC_AUX_M2 0x108 37cdfbff78SBenjamin Gaignard #define TVO_CSC_AUX_M3 0x10c 38cdfbff78SBenjamin Gaignard #define TVO_CSC_AUX_M4 0x110 39cdfbff78SBenjamin Gaignard #define TVO_CSC_AUX_M5 0x114 40cdfbff78SBenjamin Gaignard #define TVO_CSC_AUX_M6 0x118 41cdfbff78SBenjamin Gaignard #define TVO_CSC_AUX_M7 0x11c 42cdfbff78SBenjamin Gaignard #define TVO_AUX_IN_VID_FORMAT 0x130 43cdfbff78SBenjamin Gaignard #define TVO_VIP_HDF 0x400 44cdfbff78SBenjamin Gaignard #define TVO_HD_SYNC_SEL 0x418 45cdfbff78SBenjamin Gaignard #define TVO_HD_DAC_CFG_OFF 0x420 46cdfbff78SBenjamin Gaignard #define TVO_VIP_HDMI 0x500 47cdfbff78SBenjamin Gaignard #define TVO_HDMI_FORCE_COLOR_0 0x504 48cdfbff78SBenjamin Gaignard #define TVO_HDMI_FORCE_COLOR_1 0x508 49cdfbff78SBenjamin Gaignard #define TVO_HDMI_CLIP_VALUE_B_CB 0x50c 50cdfbff78SBenjamin Gaignard #define TVO_HDMI_CLIP_VALUE_Y_G 0x510 51cdfbff78SBenjamin Gaignard #define TVO_HDMI_CLIP_VALUE_R_CR 0x514 52cdfbff78SBenjamin Gaignard #define TVO_HDMI_SYNC_SEL 0x518 53cdfbff78SBenjamin Gaignard #define TVO_HDMI_DFV_OBS 0x540 54f32c4c50SBenjamin Gaignard #define TVO_VIP_DVO 0x600 55f32c4c50SBenjamin Gaignard #define TVO_DVO_SYNC_SEL 0x618 56f32c4c50SBenjamin Gaignard #define TVO_DVO_CONFIG 0x620 57cdfbff78SBenjamin Gaignard 58cdfbff78SBenjamin Gaignard #define TVO_IN_FMT_SIGNED BIT(0) 59cdfbff78SBenjamin Gaignard #define TVO_SYNC_EXT BIT(4) 60cdfbff78SBenjamin Gaignard 61cdfbff78SBenjamin Gaignard #define TVO_VIP_REORDER_R_SHIFT 24 62cdfbff78SBenjamin Gaignard #define TVO_VIP_REORDER_G_SHIFT 20 63cdfbff78SBenjamin Gaignard #define TVO_VIP_REORDER_B_SHIFT 16 64cdfbff78SBenjamin Gaignard #define TVO_VIP_REORDER_MASK 0x3 65cdfbff78SBenjamin Gaignard #define TVO_VIP_REORDER_Y_G_SEL 0 66cdfbff78SBenjamin Gaignard #define TVO_VIP_REORDER_CB_B_SEL 1 67cdfbff78SBenjamin Gaignard #define TVO_VIP_REORDER_CR_R_SEL 2 68cdfbff78SBenjamin Gaignard 69cdfbff78SBenjamin Gaignard #define TVO_VIP_CLIP_SHIFT 8 70cdfbff78SBenjamin Gaignard #define TVO_VIP_CLIP_MASK 0x7 71cdfbff78SBenjamin Gaignard #define TVO_VIP_CLIP_DISABLED 0 72cdfbff78SBenjamin Gaignard #define TVO_VIP_CLIP_EAV_SAV 1 73cdfbff78SBenjamin Gaignard #define TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y 2 74cdfbff78SBenjamin Gaignard #define TVO_VIP_CLIP_LIMITED_RANGE_CB_CR 3 75cdfbff78SBenjamin Gaignard #define TVO_VIP_CLIP_PROG_RANGE 4 76cdfbff78SBenjamin Gaignard 77cdfbff78SBenjamin Gaignard #define TVO_VIP_RND_SHIFT 4 78cdfbff78SBenjamin Gaignard #define TVO_VIP_RND_MASK 0x3 79cdfbff78SBenjamin Gaignard #define TVO_VIP_RND_8BIT_ROUNDED 0 80cdfbff78SBenjamin Gaignard #define TVO_VIP_RND_10BIT_ROUNDED 1 81cdfbff78SBenjamin Gaignard #define TVO_VIP_RND_12BIT_ROUNDED 2 82cdfbff78SBenjamin Gaignard 83cdfbff78SBenjamin Gaignard #define TVO_VIP_SEL_INPUT_MASK 0xf 84cdfbff78SBenjamin Gaignard #define TVO_VIP_SEL_INPUT_MAIN 0x0 85cdfbff78SBenjamin Gaignard #define TVO_VIP_SEL_INPUT_AUX 0x8 86cdfbff78SBenjamin Gaignard #define TVO_VIP_SEL_INPUT_FORCE_COLOR 0xf 87cdfbff78SBenjamin Gaignard #define TVO_VIP_SEL_INPUT_BYPASS_MASK 0x1 88cdfbff78SBenjamin Gaignard #define TVO_VIP_SEL_INPUT_BYPASSED 1 89cdfbff78SBenjamin Gaignard 90cdfbff78SBenjamin Gaignard #define TVO_SYNC_MAIN_VTG_SET_REF 0x00 91cdfbff78SBenjamin Gaignard #define TVO_SYNC_AUX_VTG_SET_REF 0x10 92cdfbff78SBenjamin Gaignard 93cdfbff78SBenjamin Gaignard #define TVO_SYNC_HD_DCS_SHIFT 8 94cdfbff78SBenjamin Gaignard 95f32c4c50SBenjamin Gaignard #define TVO_SYNC_DVO_PAD_HSYNC_SHIFT 8 96f32c4c50SBenjamin Gaignard #define TVO_SYNC_DVO_PAD_VSYNC_SHIFT 16 97f32c4c50SBenjamin Gaignard 985e03abc5SBenjamin Gaignard #define ENCODER_CRTC_MASK (BIT(0) | BIT(1)) 99cdfbff78SBenjamin Gaignard 10005a142c2SBich Hemon #define TVO_MIN_HD_HEIGHT 720 10105a142c2SBich Hemon 102cdfbff78SBenjamin Gaignard /* enum listing the supported output data format */ 103cdfbff78SBenjamin Gaignard enum sti_tvout_video_out_type { 104cdfbff78SBenjamin Gaignard STI_TVOUT_VIDEO_OUT_RGB, 105cdfbff78SBenjamin Gaignard STI_TVOUT_VIDEO_OUT_YUV, 106cdfbff78SBenjamin Gaignard }; 107cdfbff78SBenjamin Gaignard 108cdfbff78SBenjamin Gaignard struct sti_tvout { 109cdfbff78SBenjamin Gaignard struct device *dev; 110cdfbff78SBenjamin Gaignard struct drm_device *drm_dev; 111cdfbff78SBenjamin Gaignard void __iomem *regs; 112cdfbff78SBenjamin Gaignard struct reset_control *reset; 113cdfbff78SBenjamin Gaignard struct drm_encoder *hdmi; 114cdfbff78SBenjamin Gaignard struct drm_encoder *hda; 115f32c4c50SBenjamin Gaignard struct drm_encoder *dvo; 11683af0a48SBenjamin Gaignard bool debugfs_registered; 117cdfbff78SBenjamin Gaignard }; 118cdfbff78SBenjamin Gaignard 119cdfbff78SBenjamin Gaignard struct sti_tvout_encoder { 120cdfbff78SBenjamin Gaignard struct drm_encoder encoder; 121cdfbff78SBenjamin Gaignard struct sti_tvout *tvout; 122cdfbff78SBenjamin Gaignard }; 123cdfbff78SBenjamin Gaignard 124cdfbff78SBenjamin Gaignard #define to_sti_tvout_encoder(x) \ 125cdfbff78SBenjamin Gaignard container_of(x, struct sti_tvout_encoder, encoder) 126cdfbff78SBenjamin Gaignard 127cdfbff78SBenjamin Gaignard #define to_sti_tvout(x) to_sti_tvout_encoder(x)->tvout 128cdfbff78SBenjamin Gaignard 129cdfbff78SBenjamin Gaignard /* preformatter conversion matrix */ 130cdfbff78SBenjamin Gaignard static const u32 rgb_to_ycbcr_601[8] = { 131cdfbff78SBenjamin Gaignard 0xF927082E, 0x04C9FEAB, 0x01D30964, 0xFA95FD3D, 132cdfbff78SBenjamin Gaignard 0x0000082E, 0x00002000, 0x00002000, 0x00000000 133cdfbff78SBenjamin Gaignard }; 134cdfbff78SBenjamin Gaignard 135cdfbff78SBenjamin Gaignard /* 709 RGB to YCbCr */ 136cdfbff78SBenjamin Gaignard static const u32 rgb_to_ycbcr_709[8] = { 137cdfbff78SBenjamin Gaignard 0xF891082F, 0x0367FF40, 0x01280B71, 0xF9B1FE20, 138cdfbff78SBenjamin Gaignard 0x0000082F, 0x00002000, 0x00002000, 0x00000000 139cdfbff78SBenjamin Gaignard }; 140cdfbff78SBenjamin Gaignard 141cdfbff78SBenjamin Gaignard static u32 tvout_read(struct sti_tvout *tvout, int offset) 142cdfbff78SBenjamin Gaignard { 143cdfbff78SBenjamin Gaignard return readl(tvout->regs + offset); 144cdfbff78SBenjamin Gaignard } 145cdfbff78SBenjamin Gaignard 146cdfbff78SBenjamin Gaignard static void tvout_write(struct sti_tvout *tvout, u32 val, int offset) 147cdfbff78SBenjamin Gaignard { 148cdfbff78SBenjamin Gaignard writel(val, tvout->regs + offset); 149cdfbff78SBenjamin Gaignard } 150cdfbff78SBenjamin Gaignard 151cdfbff78SBenjamin Gaignard /** 152cdfbff78SBenjamin Gaignard * Set the clipping mode of a VIP 153cdfbff78SBenjamin Gaignard * 154cdfbff78SBenjamin Gaignard * @tvout: tvout structure 155ca279601SBenjamin Gaignard * @reg: register to set 156cdfbff78SBenjamin Gaignard * @cr_r: 157cdfbff78SBenjamin Gaignard * @y_g: 158cdfbff78SBenjamin Gaignard * @cb_b: 159cdfbff78SBenjamin Gaignard */ 160ca279601SBenjamin Gaignard static void tvout_vip_set_color_order(struct sti_tvout *tvout, int reg, 161cdfbff78SBenjamin Gaignard u32 cr_r, u32 y_g, u32 cb_b) 162cdfbff78SBenjamin Gaignard { 163ca279601SBenjamin Gaignard u32 val = tvout_read(tvout, reg); 164cdfbff78SBenjamin Gaignard 165cdfbff78SBenjamin Gaignard val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT); 166cdfbff78SBenjamin Gaignard val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT); 167cdfbff78SBenjamin Gaignard val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_B_SHIFT); 168cdfbff78SBenjamin Gaignard val |= cr_r << TVO_VIP_REORDER_R_SHIFT; 169cdfbff78SBenjamin Gaignard val |= y_g << TVO_VIP_REORDER_G_SHIFT; 170cdfbff78SBenjamin Gaignard val |= cb_b << TVO_VIP_REORDER_B_SHIFT; 171cdfbff78SBenjamin Gaignard 172ca279601SBenjamin Gaignard tvout_write(tvout, val, reg); 173cdfbff78SBenjamin Gaignard } 174cdfbff78SBenjamin Gaignard 175cdfbff78SBenjamin Gaignard /** 176cdfbff78SBenjamin Gaignard * Set the clipping mode of a VIP 177cdfbff78SBenjamin Gaignard * 178cdfbff78SBenjamin Gaignard * @tvout: tvout structure 179ca279601SBenjamin Gaignard * @reg: register to set 180cdfbff78SBenjamin Gaignard * @range: clipping range 181cdfbff78SBenjamin Gaignard */ 182ca279601SBenjamin Gaignard static void tvout_vip_set_clip_mode(struct sti_tvout *tvout, int reg, u32 range) 183cdfbff78SBenjamin Gaignard { 184ca279601SBenjamin Gaignard u32 val = tvout_read(tvout, reg); 185cdfbff78SBenjamin Gaignard 186cdfbff78SBenjamin Gaignard val &= ~(TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT); 187cdfbff78SBenjamin Gaignard val |= range << TVO_VIP_CLIP_SHIFT; 188ca279601SBenjamin Gaignard tvout_write(tvout, val, reg); 189cdfbff78SBenjamin Gaignard } 190cdfbff78SBenjamin Gaignard 191cdfbff78SBenjamin Gaignard /** 192cdfbff78SBenjamin Gaignard * Set the rounded value of a VIP 193cdfbff78SBenjamin Gaignard * 194cdfbff78SBenjamin Gaignard * @tvout: tvout structure 195ca279601SBenjamin Gaignard * @reg: register to set 196cdfbff78SBenjamin Gaignard * @rnd: rounded val per component 197cdfbff78SBenjamin Gaignard */ 198ca279601SBenjamin Gaignard static void tvout_vip_set_rnd(struct sti_tvout *tvout, int reg, u32 rnd) 199cdfbff78SBenjamin Gaignard { 200ca279601SBenjamin Gaignard u32 val = tvout_read(tvout, reg); 201cdfbff78SBenjamin Gaignard 202cdfbff78SBenjamin Gaignard val &= ~(TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT); 203cdfbff78SBenjamin Gaignard val |= rnd << TVO_VIP_RND_SHIFT; 204ca279601SBenjamin Gaignard tvout_write(tvout, val, reg); 205cdfbff78SBenjamin Gaignard } 206cdfbff78SBenjamin Gaignard 207cdfbff78SBenjamin Gaignard /** 208cdfbff78SBenjamin Gaignard * Select the VIP input 209cdfbff78SBenjamin Gaignard * 210cdfbff78SBenjamin Gaignard * @tvout: tvout structure 211ca279601SBenjamin Gaignard * @reg: register to set 212ca279601SBenjamin Gaignard * @main_path: main or auxiliary path 213cdfbff78SBenjamin Gaignard * @sel_input: selected_input (main/aux + conv) 214cdfbff78SBenjamin Gaignard */ 215cdfbff78SBenjamin Gaignard static void tvout_vip_set_sel_input(struct sti_tvout *tvout, 216ca279601SBenjamin Gaignard int reg, 217cdfbff78SBenjamin Gaignard bool main_path, 218cdfbff78SBenjamin Gaignard enum sti_tvout_video_out_type video_out) 219cdfbff78SBenjamin Gaignard { 220cdfbff78SBenjamin Gaignard u32 sel_input; 221ca279601SBenjamin Gaignard u32 val = tvout_read(tvout, reg); 222cdfbff78SBenjamin Gaignard 223cdfbff78SBenjamin Gaignard if (main_path) 224cdfbff78SBenjamin Gaignard sel_input = TVO_VIP_SEL_INPUT_MAIN; 225cdfbff78SBenjamin Gaignard else 226cdfbff78SBenjamin Gaignard sel_input = TVO_VIP_SEL_INPUT_AUX; 227cdfbff78SBenjamin Gaignard 228cdfbff78SBenjamin Gaignard switch (video_out) { 229cdfbff78SBenjamin Gaignard case STI_TVOUT_VIDEO_OUT_RGB: 230cdfbff78SBenjamin Gaignard sel_input |= TVO_VIP_SEL_INPUT_BYPASSED; 231cdfbff78SBenjamin Gaignard break; 232cdfbff78SBenjamin Gaignard case STI_TVOUT_VIDEO_OUT_YUV: 233cdfbff78SBenjamin Gaignard sel_input &= ~TVO_VIP_SEL_INPUT_BYPASSED; 234cdfbff78SBenjamin Gaignard break; 235cdfbff78SBenjamin Gaignard } 236cdfbff78SBenjamin Gaignard 237cdfbff78SBenjamin Gaignard /* on stih407 chip the sel_input bypass mode logic is inverted */ 238cdfbff78SBenjamin Gaignard sel_input = sel_input ^ TVO_VIP_SEL_INPUT_BYPASS_MASK; 239cdfbff78SBenjamin Gaignard 240cdfbff78SBenjamin Gaignard val &= ~TVO_VIP_SEL_INPUT_MASK; 241cdfbff78SBenjamin Gaignard val |= sel_input; 242ca279601SBenjamin Gaignard tvout_write(tvout, val, reg); 243cdfbff78SBenjamin Gaignard } 244cdfbff78SBenjamin Gaignard 245cdfbff78SBenjamin Gaignard /** 246cdfbff78SBenjamin Gaignard * Select the input video signed or unsigned 247cdfbff78SBenjamin Gaignard * 248cdfbff78SBenjamin Gaignard * @tvout: tvout structure 249ca279601SBenjamin Gaignard * @reg: register to set 250cdfbff78SBenjamin Gaignard * @in_vid_signed: used video input format 251cdfbff78SBenjamin Gaignard */ 252ca279601SBenjamin Gaignard static void tvout_vip_set_in_vid_fmt(struct sti_tvout *tvout, 253ca279601SBenjamin Gaignard int reg, u32 in_vid_fmt) 254cdfbff78SBenjamin Gaignard { 255ca279601SBenjamin Gaignard u32 val = tvout_read(tvout, reg); 256cdfbff78SBenjamin Gaignard 257cdfbff78SBenjamin Gaignard val &= ~TVO_IN_FMT_SIGNED; 258cdfbff78SBenjamin Gaignard val |= in_vid_fmt; 259ca279601SBenjamin Gaignard tvout_write(tvout, val, reg); 260cdfbff78SBenjamin Gaignard } 261cdfbff78SBenjamin Gaignard 262cdfbff78SBenjamin Gaignard /** 26305a142c2SBich Hemon * Set preformatter matrix 26405a142c2SBich Hemon * 26505a142c2SBich Hemon * @tvout: tvout structure 26605a142c2SBich Hemon * @mode: display mode structure 26705a142c2SBich Hemon */ 26805a142c2SBich Hemon static void tvout_preformatter_set_matrix(struct sti_tvout *tvout, 26905a142c2SBich Hemon struct drm_display_mode *mode) 27005a142c2SBich Hemon { 27105a142c2SBich Hemon unsigned int i; 27205a142c2SBich Hemon const u32 *pf_matrix; 27305a142c2SBich Hemon 27405a142c2SBich Hemon if (mode->vdisplay >= TVO_MIN_HD_HEIGHT) 27505a142c2SBich Hemon pf_matrix = rgb_to_ycbcr_709; 27605a142c2SBich Hemon else 27705a142c2SBich Hemon pf_matrix = rgb_to_ycbcr_601; 27805a142c2SBich Hemon 27905a142c2SBich Hemon for (i = 0; i < 8; i++) { 28005a142c2SBich Hemon tvout_write(tvout, *(pf_matrix + i), 28105a142c2SBich Hemon TVO_CSC_MAIN_M0 + (i * 4)); 28205a142c2SBich Hemon tvout_write(tvout, *(pf_matrix + i), 28305a142c2SBich Hemon TVO_CSC_AUX_M0 + (i * 4)); 28405a142c2SBich Hemon } 28505a142c2SBich Hemon } 28605a142c2SBich Hemon 28705a142c2SBich Hemon /** 288f32c4c50SBenjamin Gaignard * Start VIP block for DVO output 289f32c4c50SBenjamin Gaignard * 290f32c4c50SBenjamin Gaignard * @tvout: pointer on tvout structure 291f32c4c50SBenjamin Gaignard * @main_path: true if main path has to be used in the vip configuration 292f32c4c50SBenjamin Gaignard * else aux path is used. 293f32c4c50SBenjamin Gaignard */ 294f32c4c50SBenjamin Gaignard static void tvout_dvo_start(struct sti_tvout *tvout, bool main_path) 295f32c4c50SBenjamin Gaignard { 296f32c4c50SBenjamin Gaignard u32 tvo_in_vid_format; 297503290ceSVincent Abriou int val, tmp; 298f32c4c50SBenjamin Gaignard 299f32c4c50SBenjamin Gaignard dev_dbg(tvout->dev, "%s\n", __func__); 300f32c4c50SBenjamin Gaignard 301f32c4c50SBenjamin Gaignard if (main_path) { 302f32c4c50SBenjamin Gaignard DRM_DEBUG_DRIVER("main vip for DVO\n"); 303503290ceSVincent Abriou /* Select the input sync for dvo */ 304503290ceSVincent Abriou tmp = TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_DVO; 305503290ceSVincent Abriou val = tmp << TVO_SYNC_DVO_PAD_VSYNC_SHIFT; 306503290ceSVincent Abriou val |= tmp << TVO_SYNC_DVO_PAD_HSYNC_SHIFT; 307503290ceSVincent Abriou val |= tmp; 308f32c4c50SBenjamin Gaignard tvout_write(tvout, val, TVO_DVO_SYNC_SEL); 309f32c4c50SBenjamin Gaignard tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; 310f32c4c50SBenjamin Gaignard } else { 311f32c4c50SBenjamin Gaignard DRM_DEBUG_DRIVER("aux vip for DVO\n"); 312503290ceSVincent Abriou /* Select the input sync for dvo */ 313503290ceSVincent Abriou tmp = TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_DVO; 314503290ceSVincent Abriou val = tmp << TVO_SYNC_DVO_PAD_VSYNC_SHIFT; 315503290ceSVincent Abriou val |= tmp << TVO_SYNC_DVO_PAD_HSYNC_SHIFT; 316503290ceSVincent Abriou val |= tmp; 317f32c4c50SBenjamin Gaignard tvout_write(tvout, val, TVO_DVO_SYNC_SEL); 318f32c4c50SBenjamin Gaignard tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; 319f32c4c50SBenjamin Gaignard } 320f32c4c50SBenjamin Gaignard 321f32c4c50SBenjamin Gaignard /* Set color channel order */ 322f32c4c50SBenjamin Gaignard tvout_vip_set_color_order(tvout, TVO_VIP_DVO, 323f32c4c50SBenjamin Gaignard TVO_VIP_REORDER_CR_R_SEL, 324f32c4c50SBenjamin Gaignard TVO_VIP_REORDER_Y_G_SEL, 325f32c4c50SBenjamin Gaignard TVO_VIP_REORDER_CB_B_SEL); 326f32c4c50SBenjamin Gaignard 3271834b84dSVincent Abriou /* Set clipping mode */ 3281834b84dSVincent Abriou tvout_vip_set_clip_mode(tvout, TVO_VIP_DVO, TVO_VIP_CLIP_DISABLED); 329f32c4c50SBenjamin Gaignard 330f32c4c50SBenjamin Gaignard /* Set round mode (rounded to 8-bit per component) */ 331f32c4c50SBenjamin Gaignard tvout_vip_set_rnd(tvout, TVO_VIP_DVO, TVO_VIP_RND_8BIT_ROUNDED); 332f32c4c50SBenjamin Gaignard 333f32c4c50SBenjamin Gaignard /* Set input video format */ 334b4bba92dSVincent Abriou tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED); 335f32c4c50SBenjamin Gaignard 336f32c4c50SBenjamin Gaignard /* Input selection */ 337f32c4c50SBenjamin Gaignard tvout_vip_set_sel_input(tvout, TVO_VIP_DVO, main_path, 338f32c4c50SBenjamin Gaignard STI_TVOUT_VIDEO_OUT_RGB); 339f32c4c50SBenjamin Gaignard } 340f32c4c50SBenjamin Gaignard 341f32c4c50SBenjamin Gaignard /** 342cdfbff78SBenjamin Gaignard * Start VIP block for HDMI output 343cdfbff78SBenjamin Gaignard * 344cdfbff78SBenjamin Gaignard * @tvout: pointer on tvout structure 345cdfbff78SBenjamin Gaignard * @main_path: true if main path has to be used in the vip configuration 346cdfbff78SBenjamin Gaignard * else aux path is used. 347cdfbff78SBenjamin Gaignard */ 348cdfbff78SBenjamin Gaignard static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path) 349cdfbff78SBenjamin Gaignard { 350ca279601SBenjamin Gaignard u32 tvo_in_vid_format; 351cdfbff78SBenjamin Gaignard 352cdfbff78SBenjamin Gaignard dev_dbg(tvout->dev, "%s\n", __func__); 353cdfbff78SBenjamin Gaignard 354cdfbff78SBenjamin Gaignard if (main_path) { 355cdfbff78SBenjamin Gaignard DRM_DEBUG_DRIVER("main vip for hdmi\n"); 356503290ceSVincent Abriou /* select the input sync for hdmi */ 357503290ceSVincent Abriou tvout_write(tvout, 358503290ceSVincent Abriou TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDMI, 359503290ceSVincent Abriou TVO_HDMI_SYNC_SEL); 360ca279601SBenjamin Gaignard tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; 361cdfbff78SBenjamin Gaignard } else { 362cdfbff78SBenjamin Gaignard DRM_DEBUG_DRIVER("aux vip for hdmi\n"); 363503290ceSVincent Abriou /* select the input sync for hdmi */ 364503290ceSVincent Abriou tvout_write(tvout, 365503290ceSVincent Abriou TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDMI, 366503290ceSVincent Abriou TVO_HDMI_SYNC_SEL); 367ca279601SBenjamin Gaignard tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; 368cdfbff78SBenjamin Gaignard } 369cdfbff78SBenjamin Gaignard 370cdfbff78SBenjamin Gaignard /* set color channel order */ 371ca279601SBenjamin Gaignard tvout_vip_set_color_order(tvout, TVO_VIP_HDMI, 372cdfbff78SBenjamin Gaignard TVO_VIP_REORDER_CR_R_SEL, 373cdfbff78SBenjamin Gaignard TVO_VIP_REORDER_Y_G_SEL, 374cdfbff78SBenjamin Gaignard TVO_VIP_REORDER_CB_B_SEL); 375cdfbff78SBenjamin Gaignard 3761834b84dSVincent Abriou /* set clipping mode */ 3771834b84dSVincent Abriou tvout_vip_set_clip_mode(tvout, TVO_VIP_HDMI, TVO_VIP_CLIP_DISABLED); 378cdfbff78SBenjamin Gaignard 379cdfbff78SBenjamin Gaignard /* set round mode (rounded to 8-bit per component) */ 380ca279601SBenjamin Gaignard tvout_vip_set_rnd(tvout, TVO_VIP_HDMI, TVO_VIP_RND_8BIT_ROUNDED); 381cdfbff78SBenjamin Gaignard 382cdfbff78SBenjamin Gaignard /* set input video format */ 383b4bba92dSVincent Abriou tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED); 384cdfbff78SBenjamin Gaignard 385cdfbff78SBenjamin Gaignard /* input selection */ 386ca279601SBenjamin Gaignard tvout_vip_set_sel_input(tvout, TVO_VIP_HDMI, main_path, 387b4bba92dSVincent Abriou STI_TVOUT_VIDEO_OUT_RGB); 388cdfbff78SBenjamin Gaignard } 389cdfbff78SBenjamin Gaignard 390cdfbff78SBenjamin Gaignard /** 391cdfbff78SBenjamin Gaignard * Start HDF VIP and HD DAC 392cdfbff78SBenjamin Gaignard * 393cdfbff78SBenjamin Gaignard * @tvout: pointer on tvout structure 394cdfbff78SBenjamin Gaignard * @main_path: true if main path has to be used in the vip configuration 395cdfbff78SBenjamin Gaignard * else aux path is used. 396cdfbff78SBenjamin Gaignard */ 397cdfbff78SBenjamin Gaignard static void tvout_hda_start(struct sti_tvout *tvout, bool main_path) 398cdfbff78SBenjamin Gaignard { 399ca279601SBenjamin Gaignard u32 tvo_in_vid_format; 400ca279601SBenjamin Gaignard int val; 401cdfbff78SBenjamin Gaignard 402cdfbff78SBenjamin Gaignard dev_dbg(tvout->dev, "%s\n", __func__); 403cdfbff78SBenjamin Gaignard 404ca279601SBenjamin Gaignard if (main_path) { 405503290ceSVincent Abriou DRM_DEBUG_DRIVER("main vip for HDF\n"); 406503290ceSVincent Abriou /* Select the input sync for HD analog and HD DCS */ 407503290ceSVincent Abriou val = TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDDCS; 408503290ceSVincent Abriou val = val << TVO_SYNC_HD_DCS_SHIFT; 409503290ceSVincent Abriou val |= TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDF; 410ca279601SBenjamin Gaignard tvout_write(tvout, val, TVO_HD_SYNC_SEL); 411ca279601SBenjamin Gaignard tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; 412ca279601SBenjamin Gaignard } else { 413503290ceSVincent Abriou DRM_DEBUG_DRIVER("aux vip for HDF\n"); 414503290ceSVincent Abriou /* Select the input sync for HD analog and HD DCS */ 415503290ceSVincent Abriou val = TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDDCS; 416503290ceSVincent Abriou val = val << TVO_SYNC_HD_DCS_SHIFT; 417503290ceSVincent Abriou val |= TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDF; 418ca279601SBenjamin Gaignard tvout_write(tvout, val, TVO_HD_SYNC_SEL); 419ca279601SBenjamin Gaignard tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; 420cdfbff78SBenjamin Gaignard } 421cdfbff78SBenjamin Gaignard 422cdfbff78SBenjamin Gaignard /* set color channel order */ 423ca279601SBenjamin Gaignard tvout_vip_set_color_order(tvout, TVO_VIP_HDF, 424cdfbff78SBenjamin Gaignard TVO_VIP_REORDER_CR_R_SEL, 425cdfbff78SBenjamin Gaignard TVO_VIP_REORDER_Y_G_SEL, 426cdfbff78SBenjamin Gaignard TVO_VIP_REORDER_CB_B_SEL); 427cdfbff78SBenjamin Gaignard 4281834b84dSVincent Abriou /* set clipping mode */ 4291834b84dSVincent Abriou tvout_vip_set_clip_mode(tvout, TVO_VIP_HDF, TVO_VIP_CLIP_DISABLED); 430cdfbff78SBenjamin Gaignard 431cdfbff78SBenjamin Gaignard /* set round mode (rounded to 10-bit per component) */ 432ca279601SBenjamin Gaignard tvout_vip_set_rnd(tvout, TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED); 433cdfbff78SBenjamin Gaignard 434b4bba92dSVincent Abriou /* Set input video format */ 435b4bba92dSVincent Abriou tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED); 436cdfbff78SBenjamin Gaignard 437cdfbff78SBenjamin Gaignard /* Input selection */ 438ca279601SBenjamin Gaignard tvout_vip_set_sel_input(tvout, TVO_VIP_HDF, main_path, 439cdfbff78SBenjamin Gaignard STI_TVOUT_VIDEO_OUT_YUV); 440cdfbff78SBenjamin Gaignard 441cdfbff78SBenjamin Gaignard /* power up HD DAC */ 442cdfbff78SBenjamin Gaignard tvout_write(tvout, 0, TVO_HD_DAC_CFG_OFF); 443cdfbff78SBenjamin Gaignard } 444cdfbff78SBenjamin Gaignard 445b514bee7SVincent Abriou #define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \ 446b514bee7SVincent Abriou readl(tvout->regs + reg)) 447b514bee7SVincent Abriou 448b514bee7SVincent Abriou static void tvout_dbg_vip(struct seq_file *s, int val) 449b514bee7SVincent Abriou { 450b514bee7SVincent Abriou int r, g, b, tmp, mask; 451b514bee7SVincent Abriou char *const reorder[] = {"Y_G", "Cb_B", "Cr_R"}; 452b514bee7SVincent Abriou char *const clipping[] = {"No", "EAV/SAV", "Limited range RGB/Y", 453b514bee7SVincent Abriou "Limited range Cb/Cr", "decided by register"}; 454b514bee7SVincent Abriou char *const round[] = {"8-bit", "10-bit", "12-bit"}; 455b514bee7SVincent Abriou char *const input_sel[] = {"Main (color matrix enabled)", 456b514bee7SVincent Abriou "Main (color matrix by-passed)", 457b514bee7SVincent Abriou "", "", "", "", "", "", 458b514bee7SVincent Abriou "Aux (color matrix enabled)", 459b514bee7SVincent Abriou "Aux (color matrix by-passed)", 460b514bee7SVincent Abriou "", "", "", "", "", "Force value"}; 461b514bee7SVincent Abriou 462e9635133SMarkus Elfring seq_putc(s, '\t'); 463b514bee7SVincent Abriou mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT; 464b514bee7SVincent Abriou r = (val & mask) >> TVO_VIP_REORDER_R_SHIFT; 465b514bee7SVincent Abriou mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT; 466b514bee7SVincent Abriou g = (val & mask) >> TVO_VIP_REORDER_G_SHIFT; 467b514bee7SVincent Abriou mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_B_SHIFT; 468b514bee7SVincent Abriou b = (val & mask) >> TVO_VIP_REORDER_B_SHIFT; 469b514bee7SVincent Abriou seq_printf(s, "%-24s %s->%s %s->%s %s->%s\n", "Reorder:", 470b514bee7SVincent Abriou reorder[r], reorder[TVO_VIP_REORDER_CR_R_SEL], 471b514bee7SVincent Abriou reorder[g], reorder[TVO_VIP_REORDER_Y_G_SEL], 472b514bee7SVincent Abriou reorder[b], reorder[TVO_VIP_REORDER_CB_B_SEL]); 473b514bee7SVincent Abriou seq_puts(s, "\t\t\t\t\t"); 474b514bee7SVincent Abriou mask = TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT; 475b514bee7SVincent Abriou tmp = (val & mask) >> TVO_VIP_CLIP_SHIFT; 476b514bee7SVincent Abriou seq_printf(s, "%-24s %s\n", "Clipping:", clipping[tmp]); 477b514bee7SVincent Abriou seq_puts(s, "\t\t\t\t\t"); 478b514bee7SVincent Abriou mask = TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT; 479b514bee7SVincent Abriou tmp = (val & mask) >> TVO_VIP_RND_SHIFT; 480b514bee7SVincent Abriou seq_printf(s, "%-24s input data rounded to %s per component\n", 481b514bee7SVincent Abriou "Round:", round[tmp]); 482b514bee7SVincent Abriou seq_puts(s, "\t\t\t\t\t"); 483b514bee7SVincent Abriou tmp = (val & TVO_VIP_SEL_INPUT_MASK); 484b514bee7SVincent Abriou seq_printf(s, "%-24s %s", "Input selection:", input_sel[tmp]); 485b514bee7SVincent Abriou } 486b514bee7SVincent Abriou 487b514bee7SVincent Abriou static void tvout_dbg_hd_dac_cfg(struct seq_file *s, int val) 488b514bee7SVincent Abriou { 489b514bee7SVincent Abriou seq_printf(s, "\t%-24s %s", "HD DAC:", 490b514bee7SVincent Abriou val & 1 ? "disabled" : "enabled"); 491b514bee7SVincent Abriou } 492b514bee7SVincent Abriou 493b514bee7SVincent Abriou static int tvout_dbg_show(struct seq_file *s, void *data) 494b514bee7SVincent Abriou { 495b514bee7SVincent Abriou struct drm_info_node *node = s->private; 496b514bee7SVincent Abriou struct sti_tvout *tvout = (struct sti_tvout *)node->info_ent->data; 497b514bee7SVincent Abriou struct drm_crtc *crtc; 498b514bee7SVincent Abriou 499b514bee7SVincent Abriou seq_printf(s, "TVOUT: (vaddr = 0x%p)", tvout->regs); 500b514bee7SVincent Abriou 501b514bee7SVincent Abriou seq_puts(s, "\n\n HDMI encoder: "); 502b514bee7SVincent Abriou crtc = tvout->hdmi->crtc; 503b514bee7SVincent Abriou if (crtc) { 504b514bee7SVincent Abriou seq_printf(s, "connected to %s path", 505b514bee7SVincent Abriou sti_crtc_is_main(crtc) ? "main" : "aux"); 506b514bee7SVincent Abriou DBGFS_DUMP(TVO_HDMI_SYNC_SEL); 507b514bee7SVincent Abriou DBGFS_DUMP(TVO_VIP_HDMI); 508b514bee7SVincent Abriou tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_HDMI)); 509b514bee7SVincent Abriou } else { 510b514bee7SVincent Abriou seq_puts(s, "disabled"); 511b514bee7SVincent Abriou } 512b514bee7SVincent Abriou 513b514bee7SVincent Abriou seq_puts(s, "\n\n DVO encoder: "); 514b514bee7SVincent Abriou crtc = tvout->dvo->crtc; 515b514bee7SVincent Abriou if (crtc) { 516b514bee7SVincent Abriou seq_printf(s, "connected to %s path", 517b514bee7SVincent Abriou sti_crtc_is_main(crtc) ? "main" : "aux"); 518b514bee7SVincent Abriou DBGFS_DUMP(TVO_DVO_SYNC_SEL); 519b514bee7SVincent Abriou DBGFS_DUMP(TVO_DVO_CONFIG); 520b514bee7SVincent Abriou DBGFS_DUMP(TVO_VIP_DVO); 521b514bee7SVincent Abriou tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_DVO)); 522b514bee7SVincent Abriou } else { 523b514bee7SVincent Abriou seq_puts(s, "disabled"); 524b514bee7SVincent Abriou } 525b514bee7SVincent Abriou 526b514bee7SVincent Abriou seq_puts(s, "\n\n HDA encoder: "); 527b514bee7SVincent Abriou crtc = tvout->hda->crtc; 528b514bee7SVincent Abriou if (crtc) { 529b514bee7SVincent Abriou seq_printf(s, "connected to %s path", 530b514bee7SVincent Abriou sti_crtc_is_main(crtc) ? "main" : "aux"); 531b514bee7SVincent Abriou DBGFS_DUMP(TVO_HD_SYNC_SEL); 532b514bee7SVincent Abriou DBGFS_DUMP(TVO_HD_DAC_CFG_OFF); 533b514bee7SVincent Abriou tvout_dbg_hd_dac_cfg(s, 534b514bee7SVincent Abriou readl(tvout->regs + TVO_HD_DAC_CFG_OFF)); 535b514bee7SVincent Abriou DBGFS_DUMP(TVO_VIP_HDF); 536b514bee7SVincent Abriou tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_HDF)); 537b514bee7SVincent Abriou } else { 538b514bee7SVincent Abriou seq_puts(s, "disabled"); 539b514bee7SVincent Abriou } 540b514bee7SVincent Abriou 541b514bee7SVincent Abriou seq_puts(s, "\n\n main path configuration"); 542b514bee7SVincent Abriou DBGFS_DUMP(TVO_CSC_MAIN_M0); 543b514bee7SVincent Abriou DBGFS_DUMP(TVO_CSC_MAIN_M1); 544b514bee7SVincent Abriou DBGFS_DUMP(TVO_CSC_MAIN_M2); 545b514bee7SVincent Abriou DBGFS_DUMP(TVO_CSC_MAIN_M3); 546b514bee7SVincent Abriou DBGFS_DUMP(TVO_CSC_MAIN_M4); 547b514bee7SVincent Abriou DBGFS_DUMP(TVO_CSC_MAIN_M5); 548b514bee7SVincent Abriou DBGFS_DUMP(TVO_CSC_MAIN_M6); 549b514bee7SVincent Abriou DBGFS_DUMP(TVO_CSC_MAIN_M7); 550b514bee7SVincent Abriou DBGFS_DUMP(TVO_MAIN_IN_VID_FORMAT); 551b514bee7SVincent Abriou 552b514bee7SVincent Abriou seq_puts(s, "\n\n auxiliary path configuration"); 553b514bee7SVincent Abriou DBGFS_DUMP(TVO_CSC_AUX_M0); 554b514bee7SVincent Abriou DBGFS_DUMP(TVO_CSC_AUX_M2); 555b514bee7SVincent Abriou DBGFS_DUMP(TVO_CSC_AUX_M3); 556b514bee7SVincent Abriou DBGFS_DUMP(TVO_CSC_AUX_M4); 557b514bee7SVincent Abriou DBGFS_DUMP(TVO_CSC_AUX_M5); 558b514bee7SVincent Abriou DBGFS_DUMP(TVO_CSC_AUX_M6); 559b514bee7SVincent Abriou DBGFS_DUMP(TVO_CSC_AUX_M7); 560b514bee7SVincent Abriou DBGFS_DUMP(TVO_AUX_IN_VID_FORMAT); 561e9635133SMarkus Elfring seq_putc(s, '\n'); 562b514bee7SVincent Abriou return 0; 563b514bee7SVincent Abriou } 564b514bee7SVincent Abriou 565b514bee7SVincent Abriou static struct drm_info_list tvout_debugfs_files[] = { 566b514bee7SVincent Abriou { "tvout", tvout_dbg_show, 0, NULL }, 567b514bee7SVincent Abriou }; 568b514bee7SVincent Abriou 569b514bee7SVincent Abriou static int tvout_debugfs_init(struct sti_tvout *tvout, struct drm_minor *minor) 570b514bee7SVincent Abriou { 571b514bee7SVincent Abriou unsigned int i; 572b514bee7SVincent Abriou 573b514bee7SVincent Abriou for (i = 0; i < ARRAY_SIZE(tvout_debugfs_files); i++) 574b514bee7SVincent Abriou tvout_debugfs_files[i].data = tvout; 575b514bee7SVincent Abriou 576b514bee7SVincent Abriou return drm_debugfs_create_files(tvout_debugfs_files, 577b514bee7SVincent Abriou ARRAY_SIZE(tvout_debugfs_files), 578b514bee7SVincent Abriou minor->debugfs_root, minor); 579b514bee7SVincent Abriou } 580b514bee7SVincent Abriou 581cdfbff78SBenjamin Gaignard static void sti_tvout_encoder_dpms(struct drm_encoder *encoder, int mode) 582cdfbff78SBenjamin Gaignard { 583cdfbff78SBenjamin Gaignard } 584cdfbff78SBenjamin Gaignard 585cdfbff78SBenjamin Gaignard static void sti_tvout_encoder_mode_set(struct drm_encoder *encoder, 586cdfbff78SBenjamin Gaignard struct drm_display_mode *mode, 587cdfbff78SBenjamin Gaignard struct drm_display_mode *adjusted_mode) 588cdfbff78SBenjamin Gaignard { 589cdfbff78SBenjamin Gaignard } 590cdfbff78SBenjamin Gaignard 591cdfbff78SBenjamin Gaignard static void sti_tvout_encoder_destroy(struct drm_encoder *encoder) 592cdfbff78SBenjamin Gaignard { 593cdfbff78SBenjamin Gaignard struct sti_tvout_encoder *sti_encoder = to_sti_tvout_encoder(encoder); 594cdfbff78SBenjamin Gaignard 595cdfbff78SBenjamin Gaignard drm_encoder_cleanup(encoder); 596cdfbff78SBenjamin Gaignard kfree(sti_encoder); 597cdfbff78SBenjamin Gaignard } 598cdfbff78SBenjamin Gaignard 59983af0a48SBenjamin Gaignard static int sti_tvout_late_register(struct drm_encoder *encoder) 60083af0a48SBenjamin Gaignard { 60183af0a48SBenjamin Gaignard struct sti_tvout *tvout = to_sti_tvout(encoder); 60283af0a48SBenjamin Gaignard int ret; 60383af0a48SBenjamin Gaignard 60483af0a48SBenjamin Gaignard if (tvout->debugfs_registered) 60583af0a48SBenjamin Gaignard return 0; 60683af0a48SBenjamin Gaignard 60783af0a48SBenjamin Gaignard ret = tvout_debugfs_init(tvout, encoder->dev->primary); 60883af0a48SBenjamin Gaignard if (ret) 60983af0a48SBenjamin Gaignard return ret; 61083af0a48SBenjamin Gaignard 61183af0a48SBenjamin Gaignard tvout->debugfs_registered = true; 61283af0a48SBenjamin Gaignard return 0; 61383af0a48SBenjamin Gaignard } 61483af0a48SBenjamin Gaignard 61583af0a48SBenjamin Gaignard static void sti_tvout_early_unregister(struct drm_encoder *encoder) 61683af0a48SBenjamin Gaignard { 61783af0a48SBenjamin Gaignard struct sti_tvout *tvout = to_sti_tvout(encoder); 61883af0a48SBenjamin Gaignard 61983af0a48SBenjamin Gaignard if (!tvout->debugfs_registered) 62083af0a48SBenjamin Gaignard return; 62183af0a48SBenjamin Gaignard 62283af0a48SBenjamin Gaignard tvout->debugfs_registered = false; 62383af0a48SBenjamin Gaignard } 62483af0a48SBenjamin Gaignard 625cdfbff78SBenjamin Gaignard static const struct drm_encoder_funcs sti_tvout_encoder_funcs = { 626cdfbff78SBenjamin Gaignard .destroy = sti_tvout_encoder_destroy, 62783af0a48SBenjamin Gaignard .late_register = sti_tvout_late_register, 62883af0a48SBenjamin Gaignard .early_unregister = sti_tvout_early_unregister, 629cdfbff78SBenjamin Gaignard }; 630cdfbff78SBenjamin Gaignard 63105a142c2SBich Hemon static void sti_dvo_encoder_enable(struct drm_encoder *encoder) 632f32c4c50SBenjamin Gaignard { 633f32c4c50SBenjamin Gaignard struct sti_tvout *tvout = to_sti_tvout(encoder); 634f32c4c50SBenjamin Gaignard 63505a142c2SBich Hemon tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode); 63605a142c2SBich Hemon 6379e1f05b2SVincent Abriou tvout_dvo_start(tvout, sti_crtc_is_main(encoder->crtc)); 638f32c4c50SBenjamin Gaignard } 639f32c4c50SBenjamin Gaignard 640f32c4c50SBenjamin Gaignard static void sti_dvo_encoder_disable(struct drm_encoder *encoder) 641f32c4c50SBenjamin Gaignard { 642f32c4c50SBenjamin Gaignard struct sti_tvout *tvout = to_sti_tvout(encoder); 643f32c4c50SBenjamin Gaignard 644f32c4c50SBenjamin Gaignard /* Reset VIP register */ 645f32c4c50SBenjamin Gaignard tvout_write(tvout, 0x0, TVO_VIP_DVO); 646f32c4c50SBenjamin Gaignard } 647f32c4c50SBenjamin Gaignard 648f32c4c50SBenjamin Gaignard static const struct drm_encoder_helper_funcs sti_dvo_encoder_helper_funcs = { 649f32c4c50SBenjamin Gaignard .dpms = sti_tvout_encoder_dpms, 650f32c4c50SBenjamin Gaignard .mode_set = sti_tvout_encoder_mode_set, 65105a142c2SBich Hemon .enable = sti_dvo_encoder_enable, 652f32c4c50SBenjamin Gaignard .disable = sti_dvo_encoder_disable, 653f32c4c50SBenjamin Gaignard }; 654f32c4c50SBenjamin Gaignard 655f32c4c50SBenjamin Gaignard static struct drm_encoder * 656f32c4c50SBenjamin Gaignard sti_tvout_create_dvo_encoder(struct drm_device *dev, 657f32c4c50SBenjamin Gaignard struct sti_tvout *tvout) 658f32c4c50SBenjamin Gaignard { 659f32c4c50SBenjamin Gaignard struct sti_tvout_encoder *encoder; 660f32c4c50SBenjamin Gaignard struct drm_encoder *drm_encoder; 661f32c4c50SBenjamin Gaignard 662f32c4c50SBenjamin Gaignard encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL); 663f32c4c50SBenjamin Gaignard if (!encoder) 664f32c4c50SBenjamin Gaignard return NULL; 665f32c4c50SBenjamin Gaignard 666f32c4c50SBenjamin Gaignard encoder->tvout = tvout; 667f32c4c50SBenjamin Gaignard 668f32c4c50SBenjamin Gaignard drm_encoder = (struct drm_encoder *)encoder; 669f32c4c50SBenjamin Gaignard 670f32c4c50SBenjamin Gaignard drm_encoder->possible_crtcs = ENCODER_CRTC_MASK; 671f32c4c50SBenjamin Gaignard drm_encoder->possible_clones = 1 << 0; 672f32c4c50SBenjamin Gaignard 673f32c4c50SBenjamin Gaignard drm_encoder_init(dev, drm_encoder, 67413a3d91fSVille Syrjälä &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_LVDS, 67513a3d91fSVille Syrjälä NULL); 676f32c4c50SBenjamin Gaignard 677f32c4c50SBenjamin Gaignard drm_encoder_helper_add(drm_encoder, &sti_dvo_encoder_helper_funcs); 678f32c4c50SBenjamin Gaignard 679f32c4c50SBenjamin Gaignard return drm_encoder; 680f32c4c50SBenjamin Gaignard } 681f32c4c50SBenjamin Gaignard 68205a142c2SBich Hemon static void sti_hda_encoder_enable(struct drm_encoder *encoder) 683cdfbff78SBenjamin Gaignard { 684cdfbff78SBenjamin Gaignard struct sti_tvout *tvout = to_sti_tvout(encoder); 685cdfbff78SBenjamin Gaignard 68605a142c2SBich Hemon tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode); 68705a142c2SBich Hemon 6889e1f05b2SVincent Abriou tvout_hda_start(tvout, sti_crtc_is_main(encoder->crtc)); 689cdfbff78SBenjamin Gaignard } 690cdfbff78SBenjamin Gaignard 691cdfbff78SBenjamin Gaignard static void sti_hda_encoder_disable(struct drm_encoder *encoder) 692cdfbff78SBenjamin Gaignard { 693cdfbff78SBenjamin Gaignard struct sti_tvout *tvout = to_sti_tvout(encoder); 694cdfbff78SBenjamin Gaignard 695cdfbff78SBenjamin Gaignard /* reset VIP register */ 696cdfbff78SBenjamin Gaignard tvout_write(tvout, 0x0, TVO_VIP_HDF); 697cdfbff78SBenjamin Gaignard 698cdfbff78SBenjamin Gaignard /* power down HD DAC */ 699cdfbff78SBenjamin Gaignard tvout_write(tvout, 1, TVO_HD_DAC_CFG_OFF); 700cdfbff78SBenjamin Gaignard } 701cdfbff78SBenjamin Gaignard 702cdfbff78SBenjamin Gaignard static const struct drm_encoder_helper_funcs sti_hda_encoder_helper_funcs = { 703cdfbff78SBenjamin Gaignard .dpms = sti_tvout_encoder_dpms, 704cdfbff78SBenjamin Gaignard .mode_set = sti_tvout_encoder_mode_set, 70505a142c2SBich Hemon .commit = sti_hda_encoder_enable, 706cdfbff78SBenjamin Gaignard .disable = sti_hda_encoder_disable, 707cdfbff78SBenjamin Gaignard }; 708cdfbff78SBenjamin Gaignard 709cdfbff78SBenjamin Gaignard static struct drm_encoder *sti_tvout_create_hda_encoder(struct drm_device *dev, 710cdfbff78SBenjamin Gaignard struct sti_tvout *tvout) 711cdfbff78SBenjamin Gaignard { 712cdfbff78SBenjamin Gaignard struct sti_tvout_encoder *encoder; 713cdfbff78SBenjamin Gaignard struct drm_encoder *drm_encoder; 714cdfbff78SBenjamin Gaignard 715cdfbff78SBenjamin Gaignard encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL); 716cdfbff78SBenjamin Gaignard if (!encoder) 717cdfbff78SBenjamin Gaignard return NULL; 718cdfbff78SBenjamin Gaignard 719cdfbff78SBenjamin Gaignard encoder->tvout = tvout; 720cdfbff78SBenjamin Gaignard 721cdfbff78SBenjamin Gaignard drm_encoder = (struct drm_encoder *) encoder; 722cdfbff78SBenjamin Gaignard 7235e03abc5SBenjamin Gaignard drm_encoder->possible_crtcs = ENCODER_CRTC_MASK; 724cdfbff78SBenjamin Gaignard drm_encoder->possible_clones = 1 << 0; 725cdfbff78SBenjamin Gaignard 726cdfbff78SBenjamin Gaignard drm_encoder_init(dev, drm_encoder, 72713a3d91fSVille Syrjälä &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_DAC, NULL); 728cdfbff78SBenjamin Gaignard 729cdfbff78SBenjamin Gaignard drm_encoder_helper_add(drm_encoder, &sti_hda_encoder_helper_funcs); 730cdfbff78SBenjamin Gaignard 731cdfbff78SBenjamin Gaignard return drm_encoder; 732cdfbff78SBenjamin Gaignard } 733cdfbff78SBenjamin Gaignard 73405a142c2SBich Hemon static void sti_hdmi_encoder_enable(struct drm_encoder *encoder) 735cdfbff78SBenjamin Gaignard { 736cdfbff78SBenjamin Gaignard struct sti_tvout *tvout = to_sti_tvout(encoder); 737cdfbff78SBenjamin Gaignard 73805a142c2SBich Hemon tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode); 73905a142c2SBich Hemon 7409e1f05b2SVincent Abriou tvout_hdmi_start(tvout, sti_crtc_is_main(encoder->crtc)); 741cdfbff78SBenjamin Gaignard } 742cdfbff78SBenjamin Gaignard 743cdfbff78SBenjamin Gaignard static void sti_hdmi_encoder_disable(struct drm_encoder *encoder) 744cdfbff78SBenjamin Gaignard { 745cdfbff78SBenjamin Gaignard struct sti_tvout *tvout = to_sti_tvout(encoder); 746cdfbff78SBenjamin Gaignard 747cdfbff78SBenjamin Gaignard /* reset VIP register */ 748cdfbff78SBenjamin Gaignard tvout_write(tvout, 0x0, TVO_VIP_HDMI); 749cdfbff78SBenjamin Gaignard } 750cdfbff78SBenjamin Gaignard 751cdfbff78SBenjamin Gaignard static const struct drm_encoder_helper_funcs sti_hdmi_encoder_helper_funcs = { 752cdfbff78SBenjamin Gaignard .dpms = sti_tvout_encoder_dpms, 753cdfbff78SBenjamin Gaignard .mode_set = sti_tvout_encoder_mode_set, 75405a142c2SBich Hemon .commit = sti_hdmi_encoder_enable, 755cdfbff78SBenjamin Gaignard .disable = sti_hdmi_encoder_disable, 756cdfbff78SBenjamin Gaignard }; 757cdfbff78SBenjamin Gaignard 758cdfbff78SBenjamin Gaignard static struct drm_encoder *sti_tvout_create_hdmi_encoder(struct drm_device *dev, 759cdfbff78SBenjamin Gaignard struct sti_tvout *tvout) 760cdfbff78SBenjamin Gaignard { 761cdfbff78SBenjamin Gaignard struct sti_tvout_encoder *encoder; 762cdfbff78SBenjamin Gaignard struct drm_encoder *drm_encoder; 763cdfbff78SBenjamin Gaignard 764cdfbff78SBenjamin Gaignard encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL); 765cdfbff78SBenjamin Gaignard if (!encoder) 766cdfbff78SBenjamin Gaignard return NULL; 767cdfbff78SBenjamin Gaignard 768cdfbff78SBenjamin Gaignard encoder->tvout = tvout; 769cdfbff78SBenjamin Gaignard 770cdfbff78SBenjamin Gaignard drm_encoder = (struct drm_encoder *) encoder; 771cdfbff78SBenjamin Gaignard 7725e03abc5SBenjamin Gaignard drm_encoder->possible_crtcs = ENCODER_CRTC_MASK; 773cdfbff78SBenjamin Gaignard drm_encoder->possible_clones = 1 << 1; 774cdfbff78SBenjamin Gaignard 775cdfbff78SBenjamin Gaignard drm_encoder_init(dev, drm_encoder, 77613a3d91fSVille Syrjälä &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); 777cdfbff78SBenjamin Gaignard 778cdfbff78SBenjamin Gaignard drm_encoder_helper_add(drm_encoder, &sti_hdmi_encoder_helper_funcs); 779cdfbff78SBenjamin Gaignard 780cdfbff78SBenjamin Gaignard return drm_encoder; 781cdfbff78SBenjamin Gaignard } 782cdfbff78SBenjamin Gaignard 783cdfbff78SBenjamin Gaignard static void sti_tvout_create_encoders(struct drm_device *dev, 784cdfbff78SBenjamin Gaignard struct sti_tvout *tvout) 785cdfbff78SBenjamin Gaignard { 786cdfbff78SBenjamin Gaignard tvout->hdmi = sti_tvout_create_hdmi_encoder(dev, tvout); 787cdfbff78SBenjamin Gaignard tvout->hda = sti_tvout_create_hda_encoder(dev, tvout); 788f32c4c50SBenjamin Gaignard tvout->dvo = sti_tvout_create_dvo_encoder(dev, tvout); 789cdfbff78SBenjamin Gaignard } 790cdfbff78SBenjamin Gaignard 791cdfbff78SBenjamin Gaignard static void sti_tvout_destroy_encoders(struct sti_tvout *tvout) 792cdfbff78SBenjamin Gaignard { 793cdfbff78SBenjamin Gaignard if (tvout->hdmi) 794cdfbff78SBenjamin Gaignard drm_encoder_cleanup(tvout->hdmi); 795cdfbff78SBenjamin Gaignard tvout->hdmi = NULL; 796cdfbff78SBenjamin Gaignard 797cdfbff78SBenjamin Gaignard if (tvout->hda) 798cdfbff78SBenjamin Gaignard drm_encoder_cleanup(tvout->hda); 799cdfbff78SBenjamin Gaignard tvout->hda = NULL; 8000a1dc29dSVincent Abriou 8010a1dc29dSVincent Abriou if (tvout->dvo) 8020a1dc29dSVincent Abriou drm_encoder_cleanup(tvout->dvo); 8030a1dc29dSVincent Abriou tvout->dvo = NULL; 804cdfbff78SBenjamin Gaignard } 805cdfbff78SBenjamin Gaignard 806cdfbff78SBenjamin Gaignard static int sti_tvout_bind(struct device *dev, struct device *master, void *data) 807cdfbff78SBenjamin Gaignard { 808cdfbff78SBenjamin Gaignard struct sti_tvout *tvout = dev_get_drvdata(dev); 809cdfbff78SBenjamin Gaignard struct drm_device *drm_dev = data; 810cdfbff78SBenjamin Gaignard 811cdfbff78SBenjamin Gaignard tvout->drm_dev = drm_dev; 812cdfbff78SBenjamin Gaignard 813cdfbff78SBenjamin Gaignard sti_tvout_create_encoders(drm_dev, tvout); 814cdfbff78SBenjamin Gaignard 81553bdcf5fSBenjamin Gaignard return 0; 816cdfbff78SBenjamin Gaignard } 817cdfbff78SBenjamin Gaignard 818cdfbff78SBenjamin Gaignard static void sti_tvout_unbind(struct device *dev, struct device *master, 819cdfbff78SBenjamin Gaignard void *data) 820cdfbff78SBenjamin Gaignard { 82153bdcf5fSBenjamin Gaignard struct sti_tvout *tvout = dev_get_drvdata(dev); 82253bdcf5fSBenjamin Gaignard 82353bdcf5fSBenjamin Gaignard sti_tvout_destroy_encoders(tvout); 824cdfbff78SBenjamin Gaignard } 825cdfbff78SBenjamin Gaignard 826cdfbff78SBenjamin Gaignard static const struct component_ops sti_tvout_ops = { 827cdfbff78SBenjamin Gaignard .bind = sti_tvout_bind, 828cdfbff78SBenjamin Gaignard .unbind = sti_tvout_unbind, 829cdfbff78SBenjamin Gaignard }; 830cdfbff78SBenjamin Gaignard 831cdfbff78SBenjamin Gaignard static int sti_tvout_probe(struct platform_device *pdev) 832cdfbff78SBenjamin Gaignard { 833cdfbff78SBenjamin Gaignard struct device *dev = &pdev->dev; 834cdfbff78SBenjamin Gaignard struct device_node *node = dev->of_node; 835cdfbff78SBenjamin Gaignard struct sti_tvout *tvout; 836cdfbff78SBenjamin Gaignard struct resource *res; 837cdfbff78SBenjamin Gaignard 838cdfbff78SBenjamin Gaignard DRM_INFO("%s\n", __func__); 839cdfbff78SBenjamin Gaignard 840cdfbff78SBenjamin Gaignard if (!node) 841cdfbff78SBenjamin Gaignard return -ENODEV; 842cdfbff78SBenjamin Gaignard 843cdfbff78SBenjamin Gaignard tvout = devm_kzalloc(dev, sizeof(*tvout), GFP_KERNEL); 844cdfbff78SBenjamin Gaignard if (!tvout) 845cdfbff78SBenjamin Gaignard return -ENOMEM; 846cdfbff78SBenjamin Gaignard 847cdfbff78SBenjamin Gaignard tvout->dev = dev; 848cdfbff78SBenjamin Gaignard 849bc435de5SMarkus Elfring /* get memory resources */ 850cdfbff78SBenjamin Gaignard res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tvout-reg"); 851cdfbff78SBenjamin Gaignard if (!res) { 852cdfbff78SBenjamin Gaignard DRM_ERROR("Invalid glue resource\n"); 853cdfbff78SBenjamin Gaignard return -ENOMEM; 854cdfbff78SBenjamin Gaignard } 855cdfbff78SBenjamin Gaignard tvout->regs = devm_ioremap_nocache(dev, res->start, resource_size(res)); 85631f32a21SWei Yongjun if (!tvout->regs) 85731f32a21SWei Yongjun return -ENOMEM; 858cdfbff78SBenjamin Gaignard 859cdfbff78SBenjamin Gaignard /* get reset resources */ 860cdfbff78SBenjamin Gaignard tvout->reset = devm_reset_control_get(dev, "tvout"); 861cdfbff78SBenjamin Gaignard /* take tvout out of reset */ 862cdfbff78SBenjamin Gaignard if (!IS_ERR(tvout->reset)) 863cdfbff78SBenjamin Gaignard reset_control_deassert(tvout->reset); 864cdfbff78SBenjamin Gaignard 865cdfbff78SBenjamin Gaignard platform_set_drvdata(pdev, tvout); 866cdfbff78SBenjamin Gaignard 867cdfbff78SBenjamin Gaignard return component_add(dev, &sti_tvout_ops); 868cdfbff78SBenjamin Gaignard } 869cdfbff78SBenjamin Gaignard 870cdfbff78SBenjamin Gaignard static int sti_tvout_remove(struct platform_device *pdev) 871cdfbff78SBenjamin Gaignard { 872cdfbff78SBenjamin Gaignard component_del(&pdev->dev, &sti_tvout_ops); 873cdfbff78SBenjamin Gaignard return 0; 874cdfbff78SBenjamin Gaignard } 875cdfbff78SBenjamin Gaignard 8768e932cf0SKiran Padwal static const struct of_device_id tvout_of_match[] = { 877cdfbff78SBenjamin Gaignard { .compatible = "st,stih407-tvout", }, 878cdfbff78SBenjamin Gaignard { /* end node */ } 879cdfbff78SBenjamin Gaignard }; 880cdfbff78SBenjamin Gaignard MODULE_DEVICE_TABLE(of, tvout_of_match); 881cdfbff78SBenjamin Gaignard 882cdfbff78SBenjamin Gaignard struct platform_driver sti_tvout_driver = { 883cdfbff78SBenjamin Gaignard .driver = { 884cdfbff78SBenjamin Gaignard .name = "sti-tvout", 885cdfbff78SBenjamin Gaignard .owner = THIS_MODULE, 886cdfbff78SBenjamin Gaignard .of_match_table = tvout_of_match, 887cdfbff78SBenjamin Gaignard }, 888cdfbff78SBenjamin Gaignard .probe = sti_tvout_probe, 889cdfbff78SBenjamin Gaignard .remove = sti_tvout_remove, 890cdfbff78SBenjamin Gaignard }; 891cdfbff78SBenjamin Gaignard 892cdfbff78SBenjamin Gaignard MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); 893cdfbff78SBenjamin Gaignard MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); 894cdfbff78SBenjamin Gaignard MODULE_LICENSE("GPL"); 895