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