static int sti_tvout_bind(struct device *dev, struct device *master, void *data) { struct sti_tvout *tvout = dev_get_drvdata(dev); struct drm_device *drm_dev = data; unsigned int i; int ret; tvout->drm_dev = drm_dev; /* set preformatter matrix */ for (i = 0; i < 8; i++) { tvout_write(tvout, rgb_to_ycbcr_601[i], TVO_CSC_MAIN_M0 + (i * 4)); tvout_write(tvout, rgb_to_ycbcr_601[i], TVO_CSC_AUX_M0 + (i * 4)); } sti_tvout_create_encoders(drm_dev, tvout); ret = component_bind_all(dev, drm_dev); if (ret) sti_tvout_destroy_encoders(tvout); return ret; }
static void sti_hda_encoder_disable(struct drm_encoder *encoder) { struct sti_tvout *tvout = to_sti_tvout(encoder); /* reset VIP register */ tvout_write(tvout, 0x0, TVO_VIP_HDF); /* power down HD DAC */ tvout_write(tvout, 1, TVO_HD_DAC_CFG_OFF); }
/** * Start HDF VIP and HD DAC * * @tvout: pointer on tvout structure * @main_path: true if main path has to be used in the vip configuration * else aux path is used. */ static void tvout_hda_start(struct sti_tvout *tvout, bool main_path) { struct device_node *node = tvout->dev->of_node; bool sel_input_logic_inverted = false; u32 tvo_in_vid_format; int val; dev_dbg(tvout->dev, "%s\n", __func__); if (main_path) { val = TVO_SYNC_MAIN_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT; val |= TVO_SYNC_MAIN_VTG_SET_3; tvout_write(tvout, val, TVO_HD_SYNC_SEL); tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; } else { val = TVO_SYNC_AUX_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT; val |= TVO_SYNC_AUX_VTG_SET_3; tvout_write(tvout, val, TVO_HD_SYNC_SEL); tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; } /* set color channel order */ tvout_vip_set_color_order(tvout, TVO_VIP_HDF, TVO_VIP_REORDER_CR_R_SEL, TVO_VIP_REORDER_Y_G_SEL, TVO_VIP_REORDER_CB_B_SEL); /* set clipping mode (EAV/SAV clipping) */ tvout_vip_set_clip_mode(tvout, TVO_VIP_HDF, TVO_VIP_CLIP_EAV_SAV); /* set round mode (rounded to 10-bit per component) */ tvout_vip_set_rnd(tvout, TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED); if (of_device_is_compatible(node, "st,stih407-tvout")) { /* set input video format */ tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED); sel_input_logic_inverted = true; } /* Input selection */ tvout_vip_set_sel_input(tvout, TVO_VIP_HDF, main_path, sel_input_logic_inverted, STI_TVOUT_VIDEO_OUT_YUV); /* power up HD DAC */ tvout_write(tvout, 0, TVO_HD_DAC_CFG_OFF); }
static void sti_hdmi_encoder_disable(struct drm_encoder *encoder) { struct sti_tvout *tvout = to_sti_tvout(encoder); /* reset VIP register */ tvout_write(tvout, 0x0, TVO_VIP_HDMI); }
/** * Select the VIP input * * @tvout: tvout structure * @reg: register to set * @main_path: main or auxiliary path * @sel_input_logic_inverted: need to invert the logic * @sel_input: selected_input (main/aux + conv) */ static void tvout_vip_set_sel_input(struct sti_tvout *tvout, int reg, bool main_path, bool sel_input_logic_inverted, enum sti_tvout_video_out_type video_out) { u32 sel_input; u32 val = tvout_read(tvout, reg); if (main_path) sel_input = TVO_VIP_SEL_INPUT_MAIN; else sel_input = TVO_VIP_SEL_INPUT_AUX; switch (video_out) { case STI_TVOUT_VIDEO_OUT_RGB: sel_input |= TVO_VIP_SEL_INPUT_BYPASSED; break; case STI_TVOUT_VIDEO_OUT_YUV: sel_input &= ~TVO_VIP_SEL_INPUT_BYPASSED; break; } /* on stih407 chip the sel_input bypass mode logic is inverted */ if (sel_input_logic_inverted) sel_input = sel_input ^ TVO_VIP_SEL_INPUT_BYPASS_MASK; val &= ~TVO_VIP_SEL_INPUT_MASK; val |= sel_input; tvout_write(tvout, val, reg); }
static void sti_dvo_encoder_disable(struct drm_encoder *encoder) { struct sti_tvout *tvout = to_sti_tvout(encoder); /* Reset VIP register */ tvout_write(tvout, 0x0, TVO_VIP_DVO); }
/** * Start HDF VIP and HD DAC * * @tvout: pointer on tvout structure * @main_path: true if main path has to be used in the vip configuration * else aux path is used. */ static void tvout_hda_start(struct sti_tvout *tvout, bool main_path) { u32 tvo_in_vid_format; int val; dev_dbg(tvout->dev, "%s\n", __func__); if (main_path) { DRM_DEBUG_DRIVER("main vip for HDF\n"); /* Select the input sync for HD analog and HD DCS */ val = TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDDCS; val = val << TVO_SYNC_HD_DCS_SHIFT; val |= TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDF; tvout_write(tvout, val, TVO_HD_SYNC_SEL); tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; } else { DRM_DEBUG_DRIVER("aux vip for HDF\n"); /* Select the input sync for HD analog and HD DCS */ val = TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDDCS; val = val << TVO_SYNC_HD_DCS_SHIFT; val |= TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDF; tvout_write(tvout, val, TVO_HD_SYNC_SEL); tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; } /* set color channel order */ tvout_vip_set_color_order(tvout, TVO_VIP_HDF, TVO_VIP_REORDER_CR_R_SEL, TVO_VIP_REORDER_Y_G_SEL, TVO_VIP_REORDER_CB_B_SEL); /* set clipping mode */ tvout_vip_set_clip_mode(tvout, TVO_VIP_HDF, TVO_VIP_CLIP_DISABLED); /* set round mode (rounded to 10-bit per component) */ tvout_vip_set_rnd(tvout, TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED); /* Set input video format */ tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED); /* Input selection */ tvout_vip_set_sel_input(tvout, TVO_VIP_HDF, main_path, STI_TVOUT_VIDEO_OUT_YUV); /* power up HD DAC */ tvout_write(tvout, 0, TVO_HD_DAC_CFG_OFF); }
/** * Set the rounded value of a VIP * * @tvout: tvout structure * @reg: register to set * @rnd: rounded val per component */ static void tvout_vip_set_rnd(struct sti_tvout *tvout, int reg, u32 rnd) { u32 val = tvout_read(tvout, reg); val &= ~(TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT); val |= rnd << TVO_VIP_RND_SHIFT; tvout_write(tvout, val, reg); }
/** * Set the clipping mode of a VIP * * @tvout: tvout structure * @reg: register to set * @range: clipping range */ static void tvout_vip_set_clip_mode(struct sti_tvout *tvout, int reg, u32 range) { u32 val = tvout_read(tvout, reg); val &= ~(TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT); val |= range << TVO_VIP_CLIP_SHIFT; tvout_write(tvout, val, reg); }
/** * Start VIP block for DVO output * * @tvout: pointer on tvout structure * @main_path: true if main path has to be used in the vip configuration * else aux path is used. */ static void tvout_dvo_start(struct sti_tvout *tvout, bool main_path) { u32 tvo_in_vid_format; int val, tmp; dev_dbg(tvout->dev, "%s\n", __func__); if (main_path) { DRM_DEBUG_DRIVER("main vip for DVO\n"); /* Select the input sync for dvo */ tmp = TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_DVO; val = tmp << TVO_SYNC_DVO_PAD_VSYNC_SHIFT; val |= tmp << TVO_SYNC_DVO_PAD_HSYNC_SHIFT; val |= tmp; tvout_write(tvout, val, TVO_DVO_SYNC_SEL); tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; } else { DRM_DEBUG_DRIVER("aux vip for DVO\n"); /* Select the input sync for dvo */ tmp = TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_DVO; val = tmp << TVO_SYNC_DVO_PAD_VSYNC_SHIFT; val |= tmp << TVO_SYNC_DVO_PAD_HSYNC_SHIFT; val |= tmp; tvout_write(tvout, val, TVO_DVO_SYNC_SEL); tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; } /* Set color channel order */ tvout_vip_set_color_order(tvout, TVO_VIP_DVO, TVO_VIP_REORDER_CR_R_SEL, TVO_VIP_REORDER_Y_G_SEL, TVO_VIP_REORDER_CB_B_SEL); /* Set clipping mode */ tvout_vip_set_clip_mode(tvout, TVO_VIP_DVO, TVO_VIP_CLIP_DISABLED); /* Set round mode (rounded to 8-bit per component) */ tvout_vip_set_rnd(tvout, TVO_VIP_DVO, TVO_VIP_RND_8BIT_ROUNDED); /* Set input video format */ tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED); /* Input selection */ tvout_vip_set_sel_input(tvout, TVO_VIP_DVO, main_path, STI_TVOUT_VIDEO_OUT_RGB); }
/** * Select the input video signed or unsigned * * @tvout: tvout structure * @reg: register to set * @in_vid_signed: used video input format */ static void tvout_vip_set_in_vid_fmt(struct sti_tvout *tvout, int reg, u32 in_vid_fmt) { u32 val = tvout_read(tvout, reg); val &= ~TVO_IN_FMT_SIGNED; val |= in_vid_fmt; tvout_write(tvout, val, reg); }
/** * Set preformatter matrix * * @tvout: tvout structure * @mode: display mode structure */ static void tvout_preformatter_set_matrix(struct sti_tvout *tvout, struct drm_display_mode *mode) { unsigned int i; const u32 *pf_matrix; if (mode->vdisplay >= TVO_MIN_HD_HEIGHT) pf_matrix = rgb_to_ycbcr_709; else pf_matrix = rgb_to_ycbcr_601; for (i = 0; i < 8; i++) { tvout_write(tvout, *(pf_matrix + i), TVO_CSC_MAIN_M0 + (i * 4)); tvout_write(tvout, *(pf_matrix + i), TVO_CSC_AUX_M0 + (i * 4)); } }
/** * Start VIP block for HDMI output * * @tvout: pointer on tvout structure * @main_path: true if main path has to be used in the vip configuration * else aux path is used. */ static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path) { struct device_node *node = tvout->dev->of_node; bool sel_input_logic_inverted = false; u32 tvo_in_vid_format; dev_dbg(tvout->dev, "%s\n", __func__); if (main_path) { DRM_DEBUG_DRIVER("main vip for hdmi\n"); /* select the input sync for hdmi = VTG set 1 */ tvout_write(tvout, TVO_SYNC_MAIN_VTG_SET_1, TVO_HDMI_SYNC_SEL); tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; } else { DRM_DEBUG_DRIVER("aux vip for hdmi\n"); /* select the input sync for hdmi = VTG set 1 */ tvout_write(tvout, TVO_SYNC_AUX_VTG_SET_1, TVO_HDMI_SYNC_SEL); tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; } /* set color channel order */ tvout_vip_set_color_order(tvout, TVO_VIP_HDMI, TVO_VIP_REORDER_CR_R_SEL, TVO_VIP_REORDER_Y_G_SEL, TVO_VIP_REORDER_CB_B_SEL); /* set clipping mode (Limited range RGB/Y) */ tvout_vip_set_clip_mode(tvout, TVO_VIP_HDMI, TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y); /* set round mode (rounded to 8-bit per component) */ tvout_vip_set_rnd(tvout, TVO_VIP_HDMI, TVO_VIP_RND_8BIT_ROUNDED); if (of_device_is_compatible(node, "st,stih407-tvout")) { /* set input video format */ tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED); sel_input_logic_inverted = true; } /* input selection */ tvout_vip_set_sel_input(tvout, TVO_VIP_HDMI, main_path, sel_input_logic_inverted, STI_TVOUT_VIDEO_OUT_RGB); }
/** * Set the clipping mode of a VIP * * @tvout: tvout structure * @reg: register to set * @cr_r: * @y_g: * @cb_b: */ static void tvout_vip_set_color_order(struct sti_tvout *tvout, int reg, u32 cr_r, u32 y_g, u32 cb_b) { u32 val = tvout_read(tvout, reg); val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT); val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT); val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_B_SHIFT); val |= cr_r << TVO_VIP_REORDER_R_SHIFT; val |= y_g << TVO_VIP_REORDER_G_SHIFT; val |= cb_b << TVO_VIP_REORDER_B_SHIFT; tvout_write(tvout, val, reg); }