diff options
author | Dave Airlie <airlied@redhat.com> | 2015-01-21 00:17:16 (GMT) |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2015-01-21 00:17:16 (GMT) |
commit | fc83975348ebce07793e6b9f780edc3cbcffa9fc (patch) | |
tree | 0fc7972c181cebed1c344852bb8b1a96607d3b15 | |
parent | b2eb0489809cf0b824357b6fa85aab1aabe3f063 (diff) | |
parent | d50141d8072e5322ee0518a8c967b5c9caf463d2 (diff) | |
download | linux-fc83975348ebce07793e6b9f780edc3cbcffa9fc.tar.xz |
Merge tag 'imx-drm-next-2015-01-09' of git://git.pengutronix.de/git/pza/linux into drm-next
imx-drm mode fixup support, imx-hdmi bridge conversion and imx-drm cleanup
- Implement mode_fixup for a DI vertical timing limitation
- Use generic DRM OF helpers in DRM core
- Convert imx-hdmi to dw_hdmi drm_bridge and add rockchip
driver
- Add DC use counter to fix multi-display support
- Simplify handling of DI clock flags
- A few small fixes and cleanup
* tag 'imx-drm-next-2015-01-09' of git://git.pengutronix.de/git/pza/linux: (26 commits)
imx-drm: core: handling of DI clock flags to ipu_crtc_mode_set()
gpu: ipu-di: Switch to DIV_ROUND_CLOSEST for DI clock divider calc
gpu: ipu-v3: Use videomode in struct ipu_di_signal_cfg
imx-drm: encoder prepare/mode_set must use adjusted mode
imx-drm: ipuv3-crtc: Implement mode_fixup
drm_modes: add drm_display_mode_to_videomode
gpu: ipu-di: remove some non-functional code
gpu: ipu-di: Add ipu_di_adjust_videomode()
drm: rockchip: export functions needed by rockchip dw_hdmi bridge driver
drm: bridge/dw_hdmi: request interrupt only after initializing the mutes
drm: bridge/dw_hdmi: add rockchip rk3288 support
dt-bindings: Add documentation for rockchip dw hdmi
drm: bridge/dw_hdmi: add function dw_hdmi_phy_enable_spare
drm: bridge/dw_hdmi: clear i2cmphy_stat0 reg in hdmi_phy_wait_i2c_done
drm: bridge/dw_hdmi: add mode_valid support
drm: bridge/dw_hdmi: add support for multi-byte register width access
dt-bindings: add document for dw_hdmi
drm: imx: imx-hdmi: move imx-hdmi to bridge/dw_hdmi
drm: imx: imx-hdmi: split phy configuration to platform driver
drm: imx: imx-hdmi: convert imx-hdmi to drm_bridge mode
...
26 files changed, 1334 insertions, 568 deletions
diff --git a/Documentation/devicetree/bindings/drm/bridge/dw_hdmi.txt b/Documentation/devicetree/bindings/drm/bridge/dw_hdmi.txt new file mode 100644 index 0000000..a905c14 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/bridge/dw_hdmi.txt @@ -0,0 +1,50 @@ +DesignWare HDMI bridge bindings + +Required properties: +- compatible: platform specific such as: + * "snps,dw-hdmi-tx" + * "fsl,imx6q-hdmi" + * "fsl,imx6dl-hdmi" + * "rockchip,rk3288-dw-hdmi" +- reg: Physical base address and length of the controller's registers. +- interrupts: The HDMI interrupt number +- clocks, clock-names : must have the phandles to the HDMI iahb and isfr clocks, + as described in Documentation/devicetree/bindings/clock/clock-bindings.txt, + the clocks are soc specific, the clock-names should be "iahb", "isfr" +-port@[X]: SoC specific port nodes with endpoint definitions as defined + in Documentation/devicetree/bindings/media/video-interfaces.txt, + please refer to the SoC specific binding document: + * Documentation/devicetree/bindings/drm/imx/hdmi.txt + * Documentation/devicetree/bindings/video/dw_hdmi-rockchip.txt + +Optional properties +- reg-io-width: the width of the reg:1,4, default set to 1 if not present +- ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing +- clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec" + +Example: + hdmi: hdmi@0120000 { + compatible = "fsl,imx6q-hdmi"; + reg = <0x00120000 0x9000>; + interrupts = <0 115 0x04>; + gpr = <&gpr>; + clocks = <&clks 123>, <&clks 124>; + clock-names = "iahb", "isfr"; + ddc-i2c-bus = <&i2c2>; + + port@0 { + reg = <0>; + + hdmi_mux_0: endpoint { + remote-endpoint = <&ipu1_di0_hdmi>; + }; + }; + + port@1 { + reg = <1>; + + hdmi_mux_1: endpoint { + remote-endpoint = <&ipu1_di1_hdmi>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/video/dw_hdmi-rockchip.txt b/Documentation/devicetree/bindings/video/dw_hdmi-rockchip.txt new file mode 100644 index 0000000..668091f --- /dev/null +++ b/Documentation/devicetree/bindings/video/dw_hdmi-rockchip.txt @@ -0,0 +1,46 @@ +Rockchip specific extensions to the Synopsys Designware HDMI +================================ + +Required properties: +- compatible: "rockchip,rk3288-dw-hdmi"; +- reg: Physical base address and length of the controller's registers. +- clocks: phandle to hdmi iahb and isfr clocks. +- clock-names: should be "iahb" "isfr" +- rockchip,grf: this soc should set GRF regs to mux vopl/vopb. +- interrupts: HDMI interrupt number +- ports: contain a port node with endpoint definitions as defined in + Documentation/devicetree/bindings/media/video-interfaces.txt. For + vopb,set the reg = <0> and set the reg = <1> for vopl. +- reg-io-width: the width of the reg:1,4, the value should be 4 on + rk3288 platform + +Optional properties +- ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing +- clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec" + +Example: +hdmi: hdmi@ff980000 { + compatible = "rockchip,rk3288-dw-hdmi"; + reg = <0xff980000 0x20000>; + reg-io-width = <4>; + ddc-i2c-bus = <&i2c5>; + rockchip,grf = <&grf>; + interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&cru PCLK_HDMI_CTRL>, <&cru SCLK_HDMI_HDCP>; + clock-names = "iahb", "isfr"; + status = "disabled"; + ports { + hdmi_in: port { + #address-cells = <1>; + #size-cells = <0>; + hdmi_in_vopb: endpoint@0 { + reg = <0>; + remote-endpoint = <&vopb_out_hdmi>; + }; + hdmi_in_vopl: endpoint@1 { + reg = <1>; + remote-endpoint = <&vopl_out_hdmi>; + }; + }; + }; +}; diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 884923f..b70f3c8 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -3,3 +3,8 @@ config DRM_PTN3460 depends on DRM select DRM_KMS_HELPER ---help--- + +config DRM_DW_HDMI + tristate + depends on DRM + select DRM_KMS_HELPER diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index b4733e1..d8a8cfd 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -1,3 +1,4 @@ ccflags-y := -Iinclude/drm obj-$(CONFIG_DRM_PTN3460) += ptn3460.o +obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o diff --git a/drivers/gpu/drm/imx/imx-hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c index ddc53e0..6ea0005 100644 --- a/drivers/gpu/drm/imx/imx-hdmi.c +++ b/drivers/gpu/drm/bridge/dw_hdmi.c @@ -6,31 +6,26 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * SH-Mobile High-Definition Multimedia Interface (HDMI) driver - * for SLISHDMI13T and SLIPHDMIT IP cores + * Designware High-Definition Multimedia Interface (HDMI) driver * * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> */ - -#include <linux/component.h> +#include <linux/module.h> #include <linux/irq.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/clk.h> #include <linux/hdmi.h> -#include <linux/regmap.h> -#include <linux/mfd/syscon.h> -#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> #include <linux/of_device.h> +#include <drm/drm_of.h> #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_edid.h> #include <drm/drm_encoder_slave.h> -#include <video/imx-ipu-v3.h> +#include <drm/bridge/dw_hdmi.h> -#include "imx-hdmi.h" -#include "imx-drm.h" +#include "dw_hdmi.h" #define HDMI_EDID_LEN 512 @@ -54,11 +49,6 @@ enum hdmi_datamap { YCbCr422_12B = 0x12, }; -enum imx_hdmi_devtype { - IMX6Q_HDMI, - IMX6DL_HDMI, -}; - static const u16 csc_coeff_default[3][4] = { { 0x2000, 0x0000, 0x0000, 0x0000 }, { 0x0000, 0x2000, 0x0000, 0x0000 }, @@ -111,16 +101,19 @@ struct hdmi_data_info { struct hdmi_vmode video_mode; }; -struct imx_hdmi { +struct dw_hdmi { struct drm_connector connector; - struct drm_encoder encoder; + struct drm_encoder *encoder; + struct drm_bridge *bridge; - enum imx_hdmi_devtype dev_type; + enum dw_hdmi_devtype dev_type; struct device *dev; struct clk *isfr_clk; struct clk *iahb_clk; struct hdmi_data_info hdmi_data; + const struct dw_hdmi_plat_data *plat_data; + int vic; u8 edid[HDMI_EDID_LEN]; @@ -135,26 +128,42 @@ struct imx_hdmi { unsigned int sample_rate; int ratio; + + void (*write)(struct dw_hdmi *hdmi, u8 val, int offset); + u8 (*read)(struct dw_hdmi *hdmi, int offset); }; -static void imx_hdmi_set_ipu_di_mux(struct imx_hdmi *hdmi, int ipu_di) +static void dw_hdmi_writel(struct dw_hdmi *hdmi, u8 val, int offset) { - regmap_update_bits(hdmi->regmap, IOMUXC_GPR3, - IMX6Q_GPR3_HDMI_MUX_CTL_MASK, - ipu_di << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT); + writel(val, hdmi->regs + (offset << 2)); } -static inline void hdmi_writeb(struct imx_hdmi *hdmi, u8 val, int offset) +static u8 dw_hdmi_readl(struct dw_hdmi *hdmi, int offset) +{ + return readl(hdmi->regs + (offset << 2)); +} + +static void dw_hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset) { writeb(val, hdmi->regs + offset); } -static inline u8 hdmi_readb(struct imx_hdmi *hdmi, int offset) +static u8 dw_hdmi_readb(struct dw_hdmi *hdmi, int offset) { return readb(hdmi->regs + offset); } -static void hdmi_modb(struct imx_hdmi *hdmi, u8 data, u8 mask, unsigned reg) +static inline void hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset) +{ + hdmi->write(hdmi, val, offset); +} + +static inline u8 hdmi_readb(struct dw_hdmi *hdmi, int offset) +{ + return hdmi->read(hdmi, offset); +} + +static void hdmi_modb(struct dw_hdmi *hdmi, u8 data, u8 mask, unsigned reg) { u8 val = hdmi_readb(hdmi, reg) & ~mask; @@ -162,13 +171,13 @@ static void hdmi_modb(struct imx_hdmi *hdmi, u8 data, u8 mask, unsigned reg) hdmi_writeb(hdmi, val, reg); } -static void hdmi_mask_writeb(struct imx_hdmi *hdmi, u8 data, unsigned int reg, - u8 shift, u8 mask) +static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg, + u8 shift, u8 mask) { hdmi_modb(hdmi, data << shift, mask, reg); } -static void hdmi_set_clock_regenerator_n(struct imx_hdmi *hdmi, +static void hdmi_set_clock_regenerator_n(struct dw_hdmi *hdmi, unsigned int value) { hdmi_writeb(hdmi, value & 0xff, HDMI_AUD_N1); @@ -179,7 +188,7 @@ static void hdmi_set_clock_regenerator_n(struct imx_hdmi *hdmi, hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3); } -static void hdmi_regenerate_cts(struct imx_hdmi *hdmi, unsigned int cts) +static void hdmi_regenerate_cts(struct dw_hdmi *hdmi, unsigned int cts) { /* Must be set/cleared first */ hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); @@ -326,8 +335,8 @@ static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk, return (cts * ratio) / 100; } -static void hdmi_set_clk_regenerator(struct imx_hdmi *hdmi, - unsigned long pixel_clk) +static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi, + unsigned long pixel_clk) { unsigned int clk_n, clk_cts; @@ -338,7 +347,7 @@ static void hdmi_set_clk_regenerator(struct imx_hdmi *hdmi, if (!clk_cts) { dev_dbg(hdmi->dev, "%s: pixel clock not supported: %lu\n", - __func__, pixel_clk); + __func__, pixel_clk); return; } @@ -350,12 +359,12 @@ static void hdmi_set_clk_regenerator(struct imx_hdmi *hdmi, hdmi_regenerate_cts(hdmi, clk_cts); } -static void hdmi_init_clk_regenerator(struct imx_hdmi *hdmi) +static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi) { hdmi_set_clk_regenerator(hdmi, 74250000); } -static void hdmi_clk_regenerator_update_pixel_clock(struct imx_hdmi *hdmi) +static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi) { hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock); } @@ -367,7 +376,7 @@ static void hdmi_clk_regenerator_update_pixel_clock(struct imx_hdmi *hdmi) * pin{31~24} <==> G[7:0] * pin{15~8} <==> B[7:0] */ -static void hdmi_video_sample(struct imx_hdmi *hdmi) +static void hdmi_video_sample(struct dw_hdmi *hdmi) { int color_format = 0; u8 val; @@ -423,12 +432,12 @@ static void hdmi_video_sample(struct imx_hdmi *hdmi) hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA1); } -static int is_color_space_conversion(struct imx_hdmi *hdmi) +static int is_color_space_conversion(struct dw_hdmi *hdmi) { return hdmi->hdmi_data.enc_in_format != hdmi->hdmi_data.enc_out_format; } -static int is_color_space_decimation(struct imx_hdmi *hdmi) +static int is_color_space_decimation(struct dw_hdmi *hdmi) { if (hdmi->hdmi_data.enc_out_format != YCBCR422_8BITS) return 0; @@ -438,7 +447,7 @@ static int is_color_space_decimation(struct imx_hdmi *hdmi) return 0; } -static int is_color_space_interpolation(struct imx_hdmi *hdmi) +static int is_color_space_interpolation(struct dw_hdmi *hdmi) { if (hdmi->hdmi_data.enc_in_format != YCBCR422_8BITS) return 0; @@ -448,7 +457,7 @@ static int is_color_space_interpolation(struct imx_hdmi *hdmi) return 0; } -static void imx_hdmi_update_csc_coeffs(struct imx_hdmi *hdmi) +static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi) { const u16 (*csc_coeff)[3][4] = &csc_coeff_default; unsigned i; @@ -477,13 +486,11 @@ static void imx_hdmi_update_csc_coeffs(struct imx_hdmi *hdmi) u16 coeff_b = (*csc_coeff)[1][i]; u16 coeff_c = (*csc_coeff)[2][i]; - hdmi_writeb(hdmi, coeff_a & 0xff, - HDMI_CSC_COEF_A1_LSB + i * 2); + hdmi_writeb(hdmi, coeff_a & 0xff, HDMI_CSC_COEF_A1_LSB + i * 2); hdmi_writeb(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2); hdmi_writeb(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2); hdmi_writeb(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2); - hdmi_writeb(hdmi, coeff_c & 0xff, - HDMI_CSC_COEF_C1_LSB + i * 2); + hdmi_writeb(hdmi, coeff_c & 0xff, HDMI_CSC_COEF_C1_LSB + i * 2); hdmi_writeb(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2); } @@ -491,7 +498,7 @@ static void imx_hdmi_update_csc_coeffs(struct imx_hdmi *hdmi) HDMI_CSC_SCALE); } -static void hdmi_video_csc(struct imx_hdmi *hdmi) +static void hdmi_video_csc(struct dw_hdmi *hdmi) { int color_depth = 0; int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE; @@ -519,7 +526,7 @@ static void hdmi_video_csc(struct imx_hdmi *hdmi) hdmi_modb(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK, HDMI_CSC_SCALE); - imx_hdmi_update_csc_coeffs(hdmi); + dw_hdmi_update_csc_coeffs(hdmi); } /* @@ -527,7 +534,7 @@ static void hdmi_video_csc(struct imx_hdmi *hdmi) * for example, if input is YCC422 mode or repeater is used, * data should be repacked this module can be bypassed. */ -static void hdmi_video_packetize(struct imx_hdmi *hdmi) +static void hdmi_video_packetize(struct dw_hdmi *hdmi) { unsigned int color_depth = 0; unsigned int remap_size = HDMI_VP_REMAP_YCC422_16bit; @@ -535,21 +542,22 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi) struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data; u8 val, vp_conf; - if (hdmi_data->enc_out_format == RGB - || hdmi_data->enc_out_format == YCBCR444) { - if (!hdmi_data->enc_color_depth) + if (hdmi_data->enc_out_format == RGB || + hdmi_data->enc_out_format == YCBCR444) { + if (!hdmi_data->enc_color_depth) { output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS; - else if (hdmi_data->enc_color_depth == 8) { + } else if (hdmi_data->enc_color_depth == 8) { color_depth = 4; output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS; - } else if (hdmi_data->enc_color_depth == 10) + } else if (hdmi_data->enc_color_depth == 10) { color_depth = 5; - else if (hdmi_data->enc_color_depth == 12) + } else if (hdmi_data->enc_color_depth == 12) { color_depth = 6; - else if (hdmi_data->enc_color_depth == 16) + } else if (hdmi_data->enc_color_depth == 16) { color_depth = 7; - else + } else { return; + } } else if (hdmi_data->enc_out_format == YCBCR422_8BITS) { if (!hdmi_data->enc_color_depth || hdmi_data->enc_color_depth == 8) @@ -561,8 +569,9 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi) else return; output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422; - } else + } else { return; + } /* set the packetizer registers */ val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) & @@ -622,182 +631,132 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi) HDMI_VP_CONF); } -static inline void hdmi_phy_test_clear(struct imx_hdmi *hdmi, - unsigned char bit) +static inline void hdmi_phy_test_clear(struct dw_hdmi *hdmi, + unsigned char bit) { hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET, HDMI_PHY_TST0_TSTCLR_MASK, HDMI_PHY_TST0); } -static inline void hdmi_phy_test_enable(struct imx_hdmi *hdmi, - unsigned char bit) +static inline void hdmi_phy_test_enable(struct dw_hdmi *hdmi, + unsigned char bit) { hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET, HDMI_PHY_TST0_TSTEN_MASK, HDMI_PHY_TST0); } -static inline void hdmi_phy_test_clock(struct imx_hdmi *hdmi, - unsigned char bit) +static inline void hdmi_phy_test_clock(struct dw_hdmi *hdmi, + unsigned char bit) { hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET, HDMI_PHY_TST0_TSTCLK_MASK, HDMI_PHY_TST0); } -static inline void hdmi_phy_test_din(struct imx_hdmi *hdmi, - unsigned char bit) +static inline void hdmi_phy_test_din(struct dw_hdmi *hdmi, + unsigned char bit) { hdmi_writeb(hdmi, bit, HDMI_PHY_TST1); } -static inline void hdmi_phy_test_dout(struct imx_hdmi *hdmi, - unsigned char bit) +static inline void hdmi_phy_test_dout(struct dw_hdmi *hdmi, + unsigned char bit) { hdmi_writeb(hdmi, bit, HDMI_PHY_TST2); } -static bool hdmi_phy_wait_i2c_done(struct imx_hdmi *hdmi, int msec) +static bool hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, int msec) { - while ((hdmi_readb(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) { + u32 val; + + while ((val = hdmi_readb(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) { if (msec-- == 0) return false; udelay(1000); } + hdmi_writeb(hdmi, val, HDMI_IH_I2CMPHY_STAT0); + return true; } -static void __hdmi_phy_i2c_write(struct imx_hdmi *hdmi, unsigned short data, - unsigned char addr) +static void __hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, + unsigned char addr) { hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0); hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR); hdmi_writeb(hdmi, (unsigned char)(data >> 8), - HDMI_PHY_I2CM_DATAO_1_ADDR); + HDMI_PHY_I2CM_DATAO_1_ADDR); hdmi_writeb(hdmi, (unsigned char)(data >> 0), - HDMI_PHY_I2CM_DATAO_0_ADDR); + HDMI_PHY_I2CM_DATAO_0_ADDR); hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE, - HDMI_PHY_I2CM_OPERATION_ADDR); + HDMI_PHY_I2CM_OPERATION_ADDR); hdmi_phy_wait_i2c_done(hdmi, 1000); } -static int hdmi_phy_i2c_write(struct imx_hdmi *hdmi, unsigned short data, - unsigned char addr) +static int hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, + unsigned char addr) { __hdmi_phy_i2c_write(hdmi, data, addr); return 0; } -static void imx_hdmi_phy_enable_power(struct imx_hdmi *hdmi, u8 enable) +static void dw_hdmi_phy_enable_power(struct dw_hdmi *hdmi, u8 enable) { hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, HDMI_PHY_CONF0_PDZ_OFFSET, HDMI_PHY_CONF0_PDZ_MASK); } -static void imx_hdmi_phy_enable_tmds(struct imx_hdmi *hdmi, u8 enable) +static void dw_hdmi_phy_enable_tmds(struct dw_hdmi *hdmi, u8 enable) { hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, HDMI_PHY_CONF0_ENTMDS_OFFSET, HDMI_PHY_CONF0_ENTMDS_MASK); } -static void imx_hdmi_phy_gen2_pddq(struct imx_hdmi *hdmi, u8 enable) +static void dw_hdmi_phy_enable_spare(struct dw_hdmi *hdmi, u8 enable) +{ + hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_SPARECTRL_OFFSET, + HDMI_PHY_CONF0_SPARECTRL_MASK); +} + +static void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable) { hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET, HDMI_PHY_CONF0_GEN2_PDDQ_MASK); } -static void imx_hdmi_phy_gen2_txpwron(struct imx_hdmi *hdmi, u8 enable) +static void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable) { hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET, HDMI_PHY_CONF0_GEN2_TXPWRON_MASK); } -static void imx_hdmi_phy_sel_data_en_pol(struct imx_hdmi *hdmi, u8 enable) +static void dw_hdmi_phy_sel_data_en_pol(struct dw_hdmi *hdmi, u8 enable) { hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, HDMI_PHY_CONF0_SELDATAENPOL_OFFSET, HDMI_PHY_CONF0_SELDATAENPOL_MASK); } -static void imx_hdmi_phy_sel_interface_control(struct imx_hdmi *hdmi, u8 enable) +static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable) { hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, HDMI_PHY_CONF0_SELDIPIF_OFFSET, HDMI_PHY_CONF0_SELDIPIF_MASK); } -enum { - RES_8, - RES_10, - RES_12, - RES_MAX, -}; - -struct mpll_config { - unsigned long mpixelclock; - struct { - u16 cpce; - u16 gmp; - } res[RES_MAX]; -}; - -static const struct mpll_config mpll_config[] = { - { - 45250000, { - { 0x01e0, 0x0000 }, - { 0x21e1, 0x0000 }, - { 0x41e2, 0x0000 } - }, - }, { - 92500000, { - { 0x0140, 0x0005 }, - { 0x2141, 0x0005 }, - { 0x4142, 0x0005 }, - }, - }, { - 148500000, { - { 0x00a0, 0x000a }, - { 0x20a1, 0x000a }, - { 0x40a2, 0x000a }, - }, - }, { - ~0UL, { - { 0x00a0, 0x000a }, - { 0x2001, 0x000f }, - { 0x4002, 0x000f }, - }, - } -}; - -struct curr_ctrl { - unsigned long mpixelclock; - u16 curr[RES_MAX]; -}; - -static const struct curr_ctrl curr_ctrl[] = { - /* pixelclk bpp8 bpp10 bpp12 */ - { - 54000000, { 0x091c, 0x091c, 0x06dc }, - }, { - 58400000, { 0x091c, 0x06dc, 0x06dc }, - }, { - 72000000, { 0x06dc, 0x06dc, 0x091c }, - }, { - 74250000, { 0x06dc, 0x0b5c, 0x091c }, - }, { - 118800000, { 0x091c, 0x091c, 0x06dc }, - }, { - 216000000, { 0x06dc, 0x0b5c, 0x091c }, - } -}; - -static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep, +static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, unsigned char res, int cscon) { unsigned res_idx, i; u8 val, msec; + const struct dw_hdmi_mpll_config *mpll_config = + hdmi->plat_data->mpll_cfg; + const struct dw_hdmi_curr_ctrl *curr_ctrl = hdmi->plat_data->cur_ctr; + const struct dw_hdmi_sym_term *sym_term = hdmi->plat_data->sym_term; if (prep) return -EINVAL; @@ -805,13 +764,13 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep, switch (res) { case 0: /* color resolution 0 is 8 bit colour depth */ case 8: - res_idx = RES_8; + res_idx = DW_HDMI_RES_8; break; case 10: - res_idx = RES_10; + res_idx = DW_HDMI_RES_10; break; case 12: - res_idx = RES_12; + res_idx = DW_HDMI_RES_12; break; default: return -EINVAL; @@ -826,10 +785,10 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep, hdmi_writeb(hdmi, val, HDMI_MC_FLOWCTRL); /* gen2 tx power off */ - imx_hdmi_phy_gen2_txpwron(hdmi, 0); + dw_hdmi_phy_gen2_txpwron(hdmi, 0); /* gen2 pddq */ - imx_hdmi_phy_gen2_pddq(hdmi, 1); + dw_hdmi_phy_gen2_pddq(hdmi, 1); /* PHY reset */ hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ); @@ -839,11 +798,11 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep, hdmi_phy_test_clear(hdmi, 1); hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2, - HDMI_PHY_I2CM_SLAVE_ADDR); + HDMI_PHY_I2CM_SLAVE_ADDR); hdmi_phy_test_clear(hdmi, 0); /* PLL/MPLL Cfg - always match on final entry */ - for (i = 0; i < ARRAY_SIZE(mpll_config) - 1; i++) + for (i = 0; mpll_config[i].mpixelclock != (~0UL); i++) if (hdmi->hdmi_data.video_mode.mpixelclock <= mpll_config[i].mpixelclock) break; @@ -851,15 +810,14 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep, hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].cpce, 0x06); hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].gmp, 0x15); - for (i = 0; i < ARRAY_SIZE(curr_ctrl); i++) + for (i = 0; curr_ctrl[i].mpixelclock != (~0UL); i++) if (hdmi->hdmi_data.video_mode.mpixelclock <= curr_ctrl[i].mpixelclock) break; - if (i >= ARRAY_SIZE(curr_ctrl)) { - dev_err(hdmi->dev, - "Pixel clock %d - unsupported by HDMI\n", - hdmi->hdmi_data.video_mode.mpixelclock); + if (curr_ctrl[i].mpixelclock == (~0UL)) { + dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n", + hdmi->hdmi_data.video_mode.mpixelclock); return -EINVAL; } @@ -868,24 +826,34 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep, hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */ hdmi_phy_i2c_write(hdmi, 0x0006, 0x17); + + for (i = 0; sym_term[i].mpixelclock != (~0UL); i++) + if (hdmi->hdmi_data.video_mode.mpixelclock <= + sym_term[i].mpixelclock) + break; + /* RESISTANCE TERM 133Ohm Cfg */ - hdmi_phy_i2c_write(hdmi, 0x0005, 0x19); /* TXTERM */ + hdmi_phy_i2c_write(hdmi, sym_term[i].term, 0x19); /* TXTERM */ /* PREEMP Cgf 0.00 */ - hdmi_phy_i2c_write(hdmi, 0x800d, 0x09); /* CKSYMTXCTRL */ + hdmi_phy_i2c_write(hdmi, sym_term[i].sym_ctr, 0x09); /* CKSYMTXCTRL */ + /* TX/CK LVL 10 */ hdmi_phy_i2c_write(hdmi, 0x01ad, 0x0E); /* VLEVCTRL */ /* REMOVE CLK TERM */ hdmi_phy_i2c_write(hdmi, 0x8000, 0x05); /* CKCALCTRL */ - imx_hdmi_phy_enable_power(hdmi, 1); + dw_hdmi_phy_enable_power(hdmi, 1); /* toggle TMDS enable */ - imx_hdmi_phy_enable_tmds(hdmi, 0); - imx_hdmi_phy_enable_tmds(hdmi, 1); + dw_hdmi_phy_enable_tmds(hdmi, 0); + dw_hdmi_phy_enable_tmds(hdmi, 1); /* gen2 tx power on */ - imx_hdmi_phy_gen2_txpwron(hdmi, 1); - imx_hdmi_phy_gen2_pddq(hdmi, 0); + dw_hdmi_phy_gen2_txpwron(hdmi, 1); + dw_hdmi_phy_gen2_pddq(hdmi, 0); + + if (hdmi->dev_type == RK3288_HDMI) + dw_hdmi_phy_enable_spare(hdmi, 1); /*Wait for PHY PLL lock */ msec = 5; @@ -906,7 +874,7 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep, return 0; } -static int imx_hdmi_phy_init(struct imx_hdmi *hdmi) +static int dw_hdmi_phy_init(struct dw_hdmi *hdmi) { int i, ret; bool cscon = false; @@ -917,10 +885,10 @@ static int imx_hdmi_phy_init(struct imx_hdmi *hdmi) /* HDMI Phy spec says to do the phy initialization sequence twice */ for (i = 0; i < 2; i++) { - imx_hdmi_phy_sel_data_en_pol(hdmi, 1); - imx_hdmi_phy_sel_interface_control(hdmi, 0); - imx_hdmi_phy_enable_tmds(hdmi, 0); - imx_hdmi_phy_enable_power(hdmi, 0); + dw_hdmi_phy_sel_data_en_pol(hdmi, 1); + dw_hdmi_phy_sel_interface_control(hdmi, 0); + dw_hdmi_phy_enable_tmds(hdmi, 0); + dw_hdmi_phy_enable_power(hdmi, 0); /* Enable CSC */ ret = hdmi_phy_configure(hdmi, 0, 8, cscon); @@ -932,7 +900,7 @@ static int imx_hdmi_phy_init(struct imx_hdmi *hdmi) return 0; } -static void hdmi_tx_hdcp_config(struct imx_hdmi *hdmi) +static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi) { u8 de; @@ -951,7 +919,7 @@ static void hdmi_tx_hdcp_config(struct imx_hdmi *hdmi) HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1); } -static void hdmi_config_AVI(struct imx_hdmi *hdmi) +static void hdmi_config_AVI(struct dw_hdmi *hdmi) { u8 val, pix_fmt, under_scan; u8 act_ratio, coded_ratio, colorimetry, ext_colorimetry; @@ -1045,7 +1013,7 @@ static void hdmi_config_AVI(struct imx_hdmi *hdmi) hdmi_writeb(hdmi, 0, HDMI_FC_AVISRB1); } -static void hdmi_av_composer(struct imx_hdmi *hdmi, +static void hdmi_av_composer(struct dw_hdmi *hdmi, const struct drm_display_mode *mode) { u8 inv_val; @@ -1129,19 +1097,19 @@ static void hdmi_av_composer(struct imx_hdmi *hdmi, hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH); } -static void imx_hdmi_phy_disable(struct imx_hdmi *hdmi) +static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi) { if (!hdmi->phy_enabled) return; - imx_hdmi_phy_enable_tmds(hdmi, 0); - imx_hdmi_phy_enable_power(hdmi, 0); + dw_hdmi_phy_enable_tmds(hdmi, 0); + dw_hdmi_phy_enable_power(hdmi, 0); hdmi->phy_enabled = false; } /* HDMI Initialization Step B.4 */ -static void imx_hdmi_enable_video_path(struct imx_hdmi *hdmi) +static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi) { u8 clkdis; @@ -1170,13 +1138,13 @@ static void imx_hdmi_enable_video_path(struct imx_hdmi *hdmi) } } -static void hdmi_enable_audio_clk(struct imx_hdmi *hdmi) +static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi) { hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); } /* Workaround to clear the overflow condition */ -static void imx_hdmi_clear_overflow(struct imx_hdmi *hdmi) +static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi) { int count; u8 val; @@ -1194,19 +1162,19 @@ static void imx_hdmi_clear_overflow(struct imx_hdmi *hdmi) hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF); } -static void hdmi_enable_overflow_interrupts(struct imx_hdmi *hdmi) +static void hdmi_enable_overflow_interrupts(struct dw_hdmi *hdmi) { hdmi_writeb(hdmi, 0, HDMI_FC_MASK2); hdmi_writeb(hdmi, 0, HDMI_IH_MUTE_FC_STAT2); } -static void hdmi_disable_overflow_interrupts(struct imx_hdmi *hdmi) +static void hdmi_disable_overflow_interrupts(struct dw_hdmi *hdmi) { hdmi_writeb(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK, HDMI_IH_MUTE_FC_STAT2); } -static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct drm_display_mode *mode) +static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode) { int ret; @@ -1223,21 +1191,21 @@ static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct drm_display_mode *mode) } if ((hdmi->vic == 6) || (hdmi->vic == 7) || - (hdmi->vic == 21) || (hdmi->vic == 22) || - (hdmi->vic == 2) || (hdmi->vic == 3) || - (hdmi->vic == 17) || (hdmi->vic == 18)) + (hdmi->vic == 21) || (hdmi->vic == 22) || + (hdmi->vic == 2) || (hdmi->vic == 3) || + (hdmi->vic == 17) || (hdmi->vic == 18)) hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601; else hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709; if ((hdmi->vic == 10) || (hdmi->vic == 11) || - (hdmi->vic == 12) || (hdmi->vic == 13) || - (hdmi->vic == 14) || (hdmi->vic == 15) || - (hdmi->vic == 25) || (hdmi->vic == 26) || - (hdmi->vic == 27) || (hdmi->vic == 28) || - (hdmi->vic == 29) || (hdmi->vic == 30) || - (hdmi->vic == 35) || (hdmi->vic == 36) || - (hdmi->vic == 37) || (hdmi->vic == 38)) + (hdmi->vic == 12) || (hdmi->vic == 13) || + (hdmi->vic == 14) || (hdmi->vic == 15) || + (hdmi->vic == 25) || (hdmi->vic == 26) || + (hdmi->vic == 27) || (hdmi->vic == 28) || + (hdmi->vic == 29) || (hdmi->vic == 30) || + (hdmi->vic == 35) || (hdmi->vic == 36) || + (hdmi->vic == 37) || (hdmi->vic == 38)) hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1; else hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0; @@ -1258,17 +1226,17 @@ static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct drm_display_mode *mode) hdmi_av_composer(hdmi, mode); /* HDMI Initializateion Step B.2 */ - ret = imx_hdmi_phy_init(hdmi); + ret = dw_hdmi_phy_init(hdmi); if (ret) return ret; /* HDMI Initialization Step B.3 */ - imx_hdmi_enable_video_path(hdmi); + dw_hdmi_enable_video_path(hdmi); /* not for DVI mode */ - if (hdmi->hdmi_data.video_mode.mdvi) + if (hdmi->hdmi_data.video_mode.mdvi) { dev_dbg(hdmi->dev, "%s DVI mode\n", __func__); - else { + } else { dev_dbg(hdmi->dev, "%s CEA mode\n", __func__); /* HDMI Initialization Step E - Configure audio */ @@ -1284,7 +1252,7 @@ static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct drm_display_mode *mode) hdmi_video_sample(hdmi); hdmi_tx_hdcp_config(hdmi); - imx_hdmi_clear_overflow(hdmi); + dw_hdmi_clear_overflow(hdmi); if (hdmi->cable_plugin && !hdmi->hdmi_data.video_mode.mdvi) hdmi_enable_overflow_interrupts(hdmi); @@ -1292,7 +1260,7 @@ static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct drm_display_mode *mode) } /* Wait until we are registered to enable interrupts */ -static int imx_hdmi_fb_registered(struct imx_hdmi *hdmi) +static int dw_hdmi_fb_registered(struct dw_hdmi *hdmi) { hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL, HDMI_PHY_I2CM_INT_ADDR); @@ -1310,7 +1278,7 @@ static int imx_hdmi_fb_registered(struct imx_hdmi *hdmi) return 0; } -static void initialize_hdmi_ih_mutes(struct imx_hdmi *hdmi) +static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi) { u8 ih_mute; @@ -1362,29 +1330,73 @@ static void initialize_hdmi_ih_mutes(struct imx_hdmi *hdmi) hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE); } -static void imx_hdmi_poweron(struct imx_hdmi *hdmi) +static void dw_hdmi_poweron(struct dw_hdmi *hdmi) +{ + dw_hdmi_setup(hdmi, &hdmi->previous_mode); +} + +static void dw_hdmi_poweroff(struct dw_hdmi *hdmi) +{ + dw_hdmi_phy_disable(hdmi); +} + +static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *orig_mode, + struct drm_display_mode *mode) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + + dw_hdmi_setup(hdmi, mode); + + /* Store the display mode for plugin/DKMS poweron events */ + memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); +} + +static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void dw_hdmi_bridge_disable(struct drm_bridge *bridge) { - imx_hdmi_setup(hdmi, &hdmi->previous_mode); + struct dw_hdmi *hdmi = bridge->driver_private; + + dw_hdmi_poweroff(hdmi); +} + +static void dw_hdmi_bridge_enable(struct drm_bridge *bridge) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + + dw_hdmi_poweron(hdmi); } -static void imx_hdmi_poweroff(struct imx_hdmi *hdmi) +static void dw_hdmi_bridge_destroy(struct drm_bridge *bridge) { - imx_hdmi_phy_disable(hdmi); + drm_bridge_cleanup(bridge); + kfree(bridge); } -static enum drm_connector_status imx_hdmi_connector_detect(struct drm_connector - *connector, bool force) +static void dw_hdmi_bridge_nop(struct drm_bridge *bridge) { - struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi, + /* do nothing */ +} + +static enum drm_connector_status +dw_hdmi_connector_detect(struct drm_connector *connector, bool force) +{ + struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, connector); return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ? connector_status_connected : connector_status_disconnected; } -static int imx_hdmi_connector_get_modes(struct drm_connector *connector) +static int dw_hdmi_connector_get_modes(struct drm_connector *connector) { - struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi, + struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, connector); struct edid *edid; int ret; @@ -1407,94 +1419,61 @@ static int imx_hdmi_connector_get_modes(struct drm_connector *connector) return 0; } -static struct drm_encoder *imx_hdmi_connector_best_encoder(struct drm_connector - *connector) -{ - struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi, - connector); - - return &hdmi->encoder; -} - -static void imx_hdmi_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static enum drm_mode_status +dw_hdmi_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) { - struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder); + struct dw_hdmi *hdmi = container_of(connector, + struct dw_hdmi, connector); + enum drm_mode_status mode_status = MODE_OK; - imx_hdmi_setup(hdmi, mode); + if (hdmi->plat_data->mode_valid) + mode_status = hdmi->plat_data->mode_valid(connector, mode); - /* Store the display mode for plugin/DKMS poweron events */ - memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); -} - -static bool imx_hdmi_encoder_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - -static void imx_hdmi_encoder_disable(struct drm_encoder *encoder) -{ -} - -static void imx_hdmi_encoder_dpms(struct drm_encoder *encoder, int mode) -{ - struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder); - - if (mode) - imx_hdmi_poweroff(hdmi); - else - imx_hdmi_poweron(hdmi); + return mode_status; } -static void imx_hdmi_encoder_prepare(struct drm_encoder *encoder) +static struct drm_encoder *dw_hdmi_connector_best_encoder(struct drm_connector + *connector) { - struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder); + struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, + connector); - imx_hdmi_poweroff(hdmi); - imx_drm_panel_format(encoder, V4L2_PIX_FMT_RGB24); + return hdmi->encoder; } -static void imx_hdmi_encoder_commit(struct drm_encoder *encoder) +static void dw_hdmi_connector_destroy(struct drm_connector *connector) { - struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder); - int mux = imx_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder); - - imx_hdmi_set_ipu_di_mux(hdmi, mux); - - imx_hdmi_poweron(hdmi); + drm_connector_unregister(connector); + drm_connector_cleanup(connector); } -static struct drm_encoder_funcs imx_hdmi_encoder_funcs = { - .destroy = imx_drm_encoder_destroy, -}; - -static struct drm_encoder_helper_funcs imx_hdmi_encoder_helper_funcs = { - .dpms = imx_hdmi_encoder_dpms, - .prepare = imx_hdmi_encoder_prepare, - .commit = imx_hdmi_encoder_commit, - .mode_set = imx_hdmi_encoder_mode_set, - .mode_fixup = imx_hdmi_encoder_mode_fixup, - .disable = imx_hdmi_encoder_disable, -}; - -static struct drm_connector_funcs imx_hdmi_connector_funcs = { +static struct drm_connector_funcs dw_hdmi_connector_funcs = { .dpms = drm_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, - .detect = imx_hdmi_connector_detect, - .destroy = imx_drm_connector_destroy, + .detect = dw_hdmi_connector_detect, + .destroy = dw_hdmi_connector_destroy, +}; + +static struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = { + .get_modes = dw_hdmi_connector_get_modes, + .mode_valid = dw_hdmi_connector_mode_valid, + .best_encoder = dw_hdmi_connector_best_encoder, }; -static struct drm_connector_helper_funcs imx_hdmi_connector_helper_funcs = { - .get_modes = imx_hdmi_connector_get_modes, - .best_encoder = imx_hdmi_connector_best_encoder, +struct drm_bridge_funcs dw_hdmi_bridge_funcs = { + .enable = dw_hdmi_bridge_enable, + .disable = dw_hdmi_bridge_disable, + .pre_enable = dw_hdmi_bridge_nop, + .post_disable = dw_hdmi_bridge_nop, + .mode_set = dw_hdmi_bridge_mode_set, + .mode_fixup = dw_hdmi_bridge_mode_fixup, + .destroy = dw_hdmi_bridge_destroy, }; -static irqreturn_t imx_hdmi_hardirq(int irq, void *dev_id) +static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id) { - struct imx_hdmi *hdmi = dev_id; + struct dw_hdmi *hdmi = dev_id; u8 intr_stat; intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0); @@ -1504,9 +1483,9 @@ static irqreturn_t imx_hdmi_hardirq(int irq, void *dev_id) return intr_stat ? IRQ_WAKE_THREAD : IRQ_NONE; } -static irqreturn_t imx_hdmi_irq(int irq, void *dev_id) +static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) { - struct imx_hdmi *hdmi = dev_id; + struct dw_hdmi *hdmi = dev_id; u8 intr_stat; u8 phy_int_pol; @@ -1520,14 +1499,14 @@ static irqreturn_t imx_hdmi_irq(int irq, void *dev_id) hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0); - imx_hdmi_poweron(hdmi); + dw_hdmi_poweron(hdmi); } else { dev_dbg(hdmi->dev, "EVENT=plugout\n"); hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD, - HDMI_PHY_POL0); + HDMI_PHY_POL0); - imx_hdmi_poweroff(hdmi); + dw_hdmi_poweroff(hdmi); } drm_helper_hpd_irq_event(hdmi->connector.dev); } @@ -1538,147 +1517,140 @@ static irqreturn_t imx_hdmi_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi) +static int dw_hdmi_register(struct drm_device *drm, struct dw_hdmi *hdmi) { + struct drm_encoder *encoder = hdmi->encoder; + struct drm_bridge *bridge; int ret; - ret = imx_drm_encoder_parse_of(drm, &hdmi->encoder, - hdmi->dev->of_node); - if (ret) - return ret; + bridge = devm_kzalloc(drm->dev, sizeof(*bridge), GFP_KERNEL); + if (!bridge) { + DRM_ERROR("Failed to allocate drm bridge\n"); + return -ENOMEM; + } - hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD; + hdmi->bridge = bridge; + bridge->driver_private = hdmi; - drm_encoder_helper_add(&hdmi->encoder, &imx_hdmi_encoder_helper_funcs); - drm_encoder_init(drm, &hdmi->encoder, &imx_hdmi_encoder_funcs, - DRM_MODE_ENCODER_TMDS); + ret = drm_bridge_init(drm, bridge, &dw_hdmi_bridge_funcs); + if (ret) { + DRM_ERROR("Failed to initialize bridge with drm\n"); + return -EINVAL; + } + + encoder->bridge = bridge; + hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD; drm_connector_helper_add(&hdmi->connector, - &imx_hdmi_connector_helper_funcs); - drm_connector_init(drm, &hdmi->connector, &imx_hdmi_connector_funcs, + &dw_hdmi_connector_helper_funcs); + drm_connector_init(drm, &hdmi->connector, &dw_hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); - hdmi->connector.encoder = &hdmi->encoder; + hdmi->connector.encoder = encoder; - drm_mode_connector_attach_encoder(&hdmi->connector, &hdmi->encoder); + drm_mode_connector_attach_encoder(&hdmi->connector, encoder); return 0; } -static struct platform_device_id imx_hdmi_devtype[] = { - { - .name = "imx6q-hdmi", - .driver_data = IMX6Q_HDMI, - }, { - .name = "imx6dl-hdmi", - .driver_data = IMX6DL_HDMI, - }, { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(platform, imx_hdmi_devtype); - -static const struct of_device_id imx_hdmi_dt_ids[] = { -{ .compatible = "fsl,imx6q-hdmi", .data = &imx_hdmi_devtype[IMX6Q_HDMI], }, -{ .compatible = "fsl,imx6dl-hdmi", .data = &imx_hdmi_devtype[IMX6DL_HDMI], }, -{ /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, imx_hdmi_dt_ids); - -static int imx_hdmi_bind(struct device *dev, struct device *master, void *data) +int dw_hdmi_bind(struct device *dev, struct device *master, + void *data, struct drm_encoder *encoder, + struct resource *iores, int irq, + const struct dw_hdmi_plat_data *plat_data) { - struct platform_device *pdev = to_platform_device(dev); - const struct of_device_id *of_id = - of_match_device(imx_hdmi_dt_ids, dev); struct drm_device *drm = data; struct device_node *np = dev->of_node; struct device_node *ddc_node; - struct imx_hdmi *hdmi; - struct resource *iores; - int ret, irq; + struct dw_hdmi *hdmi; + int ret; + u32 val = 1; hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); if (!hdmi) return -ENOMEM; + hdmi->plat_data = plat_data; hdmi->dev = dev; + hdmi->dev_type = plat_data->dev_type; hdmi->sample_rate = 48000; hdmi->ratio = 100; + hdmi->encoder = encoder; - if (of_id) { - const struct platform_device_id *device_id = of_id->data; + of_property_read_u32(np, "reg-io-width", &val); - hdmi->dev_type = device_id->driver_data; + switch (val) { + case 4: + hdmi->write = dw_hdmi_writel; + hdmi->read = dw_hdmi_readl; + break; + case 1: + hdmi->write = dw_hdmi_writeb; + hdmi->read = dw_hdmi_readb; + break; + default: + dev_err(dev, "reg-io-width must be 1 or 4\n"); + return -EINVAL; } ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); if (ddc_node) { hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node); - if (!hdmi->ddc) + of_node_put(ddc_node); + if (!hdmi->ddc) { dev_dbg(hdmi->dev, "failed to read ddc node\n"); + return -EPROBE_DEFER; + } - of_node_put(ddc_node); } else { dev_dbg(hdmi->dev, "no ddc property found\n"); } - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - ret = devm_request_threaded_irq(dev, irq, imx_hdmi_hardirq, - imx_hdmi_irq, IRQF_SHARED, - dev_name(dev), hdmi); - if (ret) - return ret; - - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); hdmi->regs = devm_ioremap_resource(dev, iores); if (IS_ERR(hdmi->regs)) return PTR_ERR(hdmi->regs); - hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr"); - if (IS_ERR(hdmi->regmap)) - return PTR_ERR(hdmi->regmap); - hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr"); if (IS_ERR(hdmi->isfr_clk)) { ret = PTR_ERR(hdmi->isfr_clk); - dev_err(hdmi->dev, - "Unable to get HDMI isfr clk: %d\n", ret); + dev_err(hdmi->dev, "Unable to get HDMI isfr clk: %d\n", ret); return ret; } ret = clk_prepare_enable(hdmi->isfr_clk); if (ret) { - dev_err(hdmi->dev, - "Cannot enable HDMI isfr clock: %d\n", ret); + dev_err(hdmi->dev, "Cannot enable HDMI isfr clock: %d\n", ret); return ret; } hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb"); if (IS_ERR(hdmi->iahb_clk)) { ret = PTR_ERR(hdmi->iahb_clk); - dev_err(hdmi->dev, - "Unable to get HDMI iahb clk: %d\n", ret); + dev_err(hdmi->dev, "Unable to get HDMI iahb clk: %d\n", ret); goto err_isfr; } ret = clk_prepare_enable(hdmi->iahb_clk); if (ret) { - dev_err(hdmi->dev, - "Cannot enable HDMI iahb clock: %d\n", ret); + dev_err(hdmi->dev, "Cannot enable HDMI iahb clock: %d\n", ret); goto err_isfr; } /* Product and revision IDs */ dev_info(dev, - "Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n", - hdmi_readb(hdmi, HDMI_DESIGN_ID), - hdmi_readb(hdmi, HDMI_REVISION_ID), - hdmi_readb(hdmi, HDMI_PRODUCT_ID0), - hdmi_readb(hdmi, HDMI_PRODUCT_ID1)); + "Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n", + hdmi_readb(hdmi, HDMI_DESIGN_ID), + hdmi_readb(hdmi, HDMI_REVISION_ID), + hdmi_readb(hdmi, HDMI_PRODUCT_ID0), + hdmi_readb(hdmi, HDMI_PRODUCT_ID1)); initialize_hdmi_ih_mutes(hdmi); + ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq, + dw_hdmi_irq, IRQF_SHARED, + dev_name(dev), hdmi); + if (ret) + return ret; + /* * To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator * N and cts values before enabling phy @@ -1694,11 +1666,11 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data) /* Clear Hotplug interrupts */ hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); - ret = imx_hdmi_fb_registered(hdmi); + ret = dw_hdmi_fb_registered(hdmi); if (ret) goto err_iahb; - ret = imx_hdmi_register(drm, hdmi); + ret = dw_hdmi_register(drm, hdmi); if (ret) goto err_iahb; @@ -1716,51 +1688,27 @@ err_isfr: return ret; } +EXPORT_SYMBOL_GPL(dw_hdmi_bind); -static void imx_hdmi_unbind(struct device *dev, struct device *master, - void *data) +void dw_hdmi_unbind(struct device *dev, struct device *master, void *data) { - struct imx_hdmi *hdmi = dev_get_drvdata(dev); + struct dw_hdmi *hdmi = dev_get_drvdata(dev); /* Disable all interrupts */ hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); hdmi->connector.funcs->destroy(&hdmi->connector); - hdmi->encoder.funcs->destroy(&hdmi->encoder); + hdmi->encoder->funcs->destroy(hdmi->encoder); clk_disable_unprepare(hdmi->iahb_clk); clk_disable_unprepare(hdmi->isfr_clk); i2c_put_adapter(hdmi->ddc); } - -static const struct component_ops hdmi_ops = { - .bind = imx_hdmi_bind, - .unbind = imx_hdmi_unbind, -}; - -static int imx_hdmi_platform_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &hdmi_ops); -} - -static int imx_hdmi_platform_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &hdmi_ops); - return 0; -} - -static struct platform_driver imx_hdmi_driver = { - .probe = imx_hdmi_platform_probe, - .remove = imx_hdmi_platform_remove, - .driver = { - .name = "imx-hdmi", - .of_match_table = imx_hdmi_dt_ids, - }, -}; - -module_platform_driver(imx_hdmi_driver); +EXPORT_SYMBOL_GPL(dw_hdmi_unbind); MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); -MODULE_DESCRIPTION("i.MX6 HDMI transmitter driver"); +MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>"); +MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>"); +MODULE_DESCRIPTION("DW HDMI transmitter driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:imx-hdmi"); +MODULE_ALIAS("platform:dw-hdmi"); diff --git a/drivers/gpu/drm/imx/imx-hdmi.h b/drivers/gpu/drm/bridge/dw_hdmi.h index 39b6776..175dbc8 100644 --- a/drivers/gpu/drm/imx/imx-hdmi.h +++ b/drivers/gpu/drm/bridge/dw_hdmi.h @@ -837,7 +837,8 @@ enum { HDMI_PHY_CONF0_PDZ_OFFSET = 7, HDMI_PHY_CONF0_ENTMDS_MASK = 0x40, HDMI_PHY_CONF0_ENTMDS_OFFSET = 6, - HDMI_PHY_CONF0_SPARECTRL = 0x20, + HDMI_PHY_CONF0_SPARECTRL_MASK = 0x20, + HDMI_PHY_CONF0_SPARECTRL_OFFSET = 5, HDMI_PHY_CONF0_GEN2_PDDQ_MASK = 0x10, HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET = 4, HDMI_PHY_CONF0_GEN2_TXPWRON_MASK = 0x8, @@ -1029,4 +1030,5 @@ enum { HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH = 0x2, HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW = 0x0, }; + #endif /* __IMX_HDMI_H__ */ diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 5125aa9..20d977a 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -615,6 +615,46 @@ void drm_display_mode_from_videomode(const struct videomode *vm, } EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode); +/** + * drm_display_mode_to_videomode - fill in @vm using @dmode, + * @dmode: drm_display_mode structure to use as source + * @vm: videomode structure to use as destination + * + * Fills out @vm using the display mode specified in @dmode. + */ +void drm_display_mode_to_videomode(const struct drm_display_mode *dmode, + struct videomode *vm) +{ + vm->hactive = dmode->hdisplay; + vm->hfront_porch = dmode->hsync_start - dmode->hdisplay; + vm->hsync_len = dmode->hsync_end - dmode->hsync_start; + vm->hback_porch = dmode->htotal - dmode->hsync_end; + + vm->vactive = dmode->vdisplay; + vm->vfront_porch = dmode->vsync_start - dmode->vdisplay; + vm->vsync_len = dmode->vsync_end - dmode->vsync_start; + vm->vback_porch = dmode->vtotal - dmode->vsync_end; + + vm->pixelclock = dmode->clock * 1000; + + vm->flags = 0; + if (dmode->flags & DRM_MODE_FLAG_PHSYNC) + vm->flags |= DISPLAY_FLAGS_HSYNC_HIGH; + else if (dmode->flags & DRM_MODE_FLAG_NHSYNC) + vm->flags |= DISPLAY_FLAGS_HSYNC_LOW; + if (dmode->flags & DRM_MODE_FLAG_PVSYNC) + vm->flags |= DISPLAY_FLAGS_VSYNC_HIGH; + else if (dmode->flags & DRM_MODE_FLAG_NVSYNC) + vm->flags |= DISPLAY_FLAGS_VSYNC_LOW; + if (dmode->flags & DRM_MODE_FLAG_INTERLACE) + vm->flags |= DISPLAY_FLAGS_INTERLACED; + if (dmode->flags & DRM_MODE_FLAG_DBLSCAN) + vm->flags |= DISPLAY_FLAGS_DOUBLESCAN; + if (dmode->flags & DRM_MODE_FLAG_DBLCLK) + vm->flags |= DISPLAY_FLAGS_DOUBLECLK; +} +EXPORT_SYMBOL_GPL(drm_display_mode_to_videomode); + #ifdef CONFIG_OF /** * of_get_drm_display_mode - get a drm_display_mode from devicetree diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig index ab31848..5d5e4092 100644 --- a/drivers/gpu/drm/imx/Kconfig +++ b/drivers/gpu/drm/imx/Kconfig @@ -49,6 +49,7 @@ config DRM_IMX_IPUV3 config DRM_IMX_HDMI tristate "Freescale i.MX DRM HDMI" + select DRM_DW_HDMI depends on DRM_IMX help Choose this if you want to use HDMI on i.MX6. diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile index 582c438..f3ecd89 100644 --- a/drivers/gpu/drm/imx/Makefile +++ b/drivers/gpu/drm/imx/Makefile @@ -9,4 +9,4 @@ obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o imx-ipuv3-crtc-objs := ipuv3-crtc.o ipuv3-plane.o obj-$(CONFIG_DRM_IMX_IPUV3) += imx-ipuv3-crtc.o -obj-$(CONFIG_DRM_IMX_HDMI) += imx-hdmi.o +obj-$(CONFIG_DRM_IMX_HDMI) += dw_hdmi-imx.o diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c new file mode 100644 index 0000000..121d30c --- /dev/null +++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c @@ -0,0 +1,258 @@ +/* Copyright (C) 2011-2013 Freescale Semiconductor, Inc. + * + * derived from imx-hdmi.c(renamed to bridge/dw_hdmi.c now) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/component.h> +#include <linux/mfd/syscon.h> +#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> +#include <drm/bridge/dw_hdmi.h> +#include <video/imx-ipu-v3.h> +#include <linux/regmap.h> +#include <drm/drm_of.h> +#include <drm/drmP.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_encoder_slave.h> + +#include "imx-drm.h" + +struct imx_hdmi { + struct device *dev; + struct drm_encoder encoder; + struct regmap *regmap; +}; + +static const struct dw_hdmi_mpll_config imx_mpll_cfg[] = { + { + 45250000, { + { 0x01e0, 0x0000 }, + { 0x21e1, 0x0000 }, + { 0x41e2, 0x0000 } + }, + }, { + 92500000, { + { 0x0140, 0x0005 }, + { 0x2141, 0x0005 }, + { 0x4142, 0x0005 }, + }, + }, { + 148500000, { + { 0x00a0, 0x000a }, + { 0x20a1, 0x000a }, + { 0x40a2, 0x000a }, + }, + }, { + ~0UL, { + { 0x00a0, 0x000a }, + { 0x2001, 0x000f }, + { 0x4002, 0x000f }, + }, + } +}; + +static const struct dw_hdmi_curr_ctrl imx_cur_ctr[] = { + /* pixelclk bpp8 bpp10 bpp12 */ + { + 54000000, { 0x091c, 0x091c, 0x06dc }, + }, { + 58400000, { 0x091c, 0x06dc, 0x06dc }, + }, { + 72000000, { 0x06dc, 0x06dc, 0x091c }, + }, { + 74250000, { 0x06dc, 0x0b5c, 0x091c }, + }, { + 118800000, { 0x091c, 0x091c, 0x06dc }, + }, { + 216000000, { 0x06dc, 0x0b5c, 0x091c }, + } +}; + +static const struct dw_hdmi_sym_term imx_sym_term[] = { + /*pixelclk symbol term*/ + { 148500000, 0x800d, 0x0005 }, + { ~0UL, 0x0000, 0x0000 } +}; + +static int dw_hdmi_imx_parse_dt(struct imx_hdmi *hdmi) +{ + struct device_node *np = hdmi->dev->of_node; + + hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr"); + if (IS_ERR(hdmi->regmap)) { + dev_err(hdmi->dev, "Unable to get gpr\n"); + return PTR_ERR(hdmi->regmap); + } + + return 0; +} + +static void dw_hdmi_imx_encoder_disable(struct drm_encoder *encoder) +{ +} + +static bool dw_hdmi_imx_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ + return true; +} + +static void dw_hdmi_imx_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ +} + +static void dw_hdmi_imx_encoder_commit(struct drm_encoder *encoder) +{ + struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder); + int mux = imx_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder); + + regmap_update_bits(hdmi->regmap, IOMUXC_GPR3, + IMX6Q_GPR3_HDMI_MUX_CTL_MASK, + mux << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT); +} + +static void dw_hdmi_imx_encoder_prepare(struct drm_encoder *encoder) +{ + imx_drm_panel_format(encoder, V4L2_PIX_FMT_RGB24); +} + +static struct drm_encoder_helper_funcs dw_hdmi_imx_encoder_helper_funcs = { + .mode_fixup = dw_hdmi_imx_encoder_mode_fixup, + .mode_set = dw_hdmi_imx_encoder_mode_set, + .prepare = dw_hdmi_imx_encoder_prepare, + .commit = dw_hdmi_imx_encoder_commit, + .disable = dw_hdmi_imx_encoder_disable, +}; + +static struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static struct dw_hdmi_plat_data imx6q_hdmi_drv_data = { + .mpll_cfg = imx_mpll_cfg, + .cur_ctr = imx_cur_ctr, + .sym_term = imx_sym_term, + .dev_type = IMX6Q_HDMI, +}; + +static struct dw_hdmi_plat_data imx6dl_hdmi_drv_data = { + .mpll_cfg = imx_mpll_cfg, + .cur_ctr = imx_cur_ctr, + .sym_term = imx_sym_term, + .dev_type = IMX6DL_HDMI, +}; + +static const struct of_device_id dw_hdmi_imx_dt_ids[] = { + { .compatible = "fsl,imx6q-hdmi", + .data = &imx6q_hdmi_drv_data + }, { + .compatible = "fsl,imx6dl-hdmi", + .data = &imx6dl_hdmi_drv_data + }, + {}, +}; +MODULE_DEVICE_TABLE(of, dw_hdmi_imx_dt_ids); + +static int dw_hdmi_imx_bind(struct device *dev, struct device *master, + void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + const struct dw_hdmi_plat_data *plat_data; + const struct of_device_id *match; + struct drm_device *drm = data; + struct drm_encoder *encoder; + struct imx_hdmi *hdmi; + struct resource *iores; + int irq; + int ret; + + if (!pdev->dev.of_node) + return -ENODEV; + + hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); + if (!hdmi) + return -ENOMEM; + + match = of_match_node(dw_hdmi_imx_dt_ids, pdev->dev.of_node); + plat_data = match->data; + hdmi->dev = &pdev->dev; + encoder = &hdmi->encoder; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iores) + return -ENXIO; + + platform_set_drvdata(pdev, hdmi); + + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); + /* + * If we failed to find the CRTC(s) which this encoder is + * supposed to be connected to, it's because the CRTC has + * not been registered yet. Defer probing, and hope that + * the required CRTC is added later. + */ + if (encoder->possible_crtcs == 0) + return -EPROBE_DEFER; + + ret = dw_hdmi_imx_parse_dt(hdmi); + if (ret < 0) + return ret; + + drm_encoder_helper_add(encoder, &dw_hdmi_imx_encoder_helper_funcs); + drm_encoder_init(drm, encoder, &dw_hdmi_imx_encoder_funcs, + DRM_MODE_ENCODER_TMDS); + + return dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data); +} + +static void dw_hdmi_imx_unbind(struct device *dev, struct device *master, + void *data) +{ + return dw_hdmi_unbind(dev, master, data); +} + +static const struct component_ops dw_hdmi_imx_ops = { + .bind = dw_hdmi_imx_bind, + .unbind = dw_hdmi_imx_unbind, +}; + +static int dw_hdmi_imx_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &dw_hdmi_imx_ops); +} + +static int dw_hdmi_imx_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dw_hdmi_imx_ops); + + return 0; +} + +static struct platform_driver dw_hdmi_imx_platform_driver = { + .probe = dw_hdmi_imx_probe, + .remove = dw_hdmi_imx_remove, + .driver = { + .name = "dwhdmi-imx", + .of_match_table = dw_hdmi_imx_dt_ids, + }, +}; + +module_platform_driver(dw_hdmi_imx_platform_driver); + +MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>"); +MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>"); +MODULE_DESCRIPTION("IMX6 Specific DW-HDMI Driver Extension"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:dwhdmi-imx"); diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index b250130..a002f53 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -25,6 +25,7 @@ #include <drm/drm_gem_cma_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_plane_helper.h> +#include <drm/drm_of.h> #include "imx-drm.h" @@ -46,7 +47,6 @@ struct imx_drm_crtc { struct drm_crtc *crtc; int pipe; struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs; - struct device_node *port; }; static int legacyfb_depth = 16; @@ -116,8 +116,7 @@ int imx_drm_panel_format_pins(struct drm_encoder *encoder, helper = &imx_crtc->imx_drm_helper_funcs; if (helper->set_interface_pix_fmt) return helper->set_interface_pix_fmt(encoder->crtc, - encoder->encoder_type, interface_pix_fmt, - hsync_pin, vsync_pin); + interface_pix_fmt, hsync_pin, vsync_pin); return 0; } EXPORT_SYMBOL_GPL(imx_drm_panel_format_pins); @@ -365,9 +364,10 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, imx_drm_crtc->imx_drm_helper_funcs = *imx_drm_helper_funcs; imx_drm_crtc->pipe = imxdrm->pipes++; - imx_drm_crtc->port = port; imx_drm_crtc->crtc = crtc; + crtc->port = port; + imxdrm->crtc[imx_drm_crtc->pipe] = imx_drm_crtc; *new_crtc = imx_drm_crtc; @@ -408,33 +408,28 @@ int imx_drm_remove_crtc(struct imx_drm_crtc *imx_drm_crtc) } EXPORT_SYMBOL_GPL(imx_drm_remove_crtc); -/* - * Find the DRM CRTC possible mask for the connected endpoint. - * - * The encoder possible masks are defined by their position in the - * mode_config crtc_list. This means that CRTCs must not be added - * or removed once the DRM device has been fully initialised. - */ -static uint32_t imx_drm_find_crtc_mask(struct imx_drm_device *imxdrm, - struct device_node *endpoint) +int imx_drm_encoder_parse_of(struct drm_device *drm, + struct drm_encoder *encoder, struct device_node *np) { - struct device_node *port; - unsigned i; + uint32_t crtc_mask = drm_of_find_possible_crtcs(drm, np); - port = of_graph_get_remote_port(endpoint); - if (!port) - return 0; - of_node_put(port); + /* + * If we failed to find the CRTC(s) which this encoder is + * supposed to be connected to, it's because the CRTC has + * not been registered yet. Defer probing, and hope that + * the required CRTC is added later. + */ + if (crtc_mask == 0) + return -EPROBE_DEFER; - for (i = 0; i < MAX_CRTC; i++) { - struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[i]; + encoder->possible_crtcs = crtc_mask; - if (imx_drm_crtc && imx_drm_crtc->port == port) - return drm_crtc_mask(imx_drm_crtc->crtc); - } + /* FIXME: this is the mask of outputs which can clone this output. */ + encoder->possible_clones = ~0; return 0; } +EXPORT_SYMBOL_GPL(imx_drm_encoder_parse_of); static struct device_node *imx_drm_of_get_next_endpoint( const struct device_node *parent, struct device_node *prev) @@ -445,48 +440,6 @@ static struct device_node *imx_drm_of_get_next_endpoint( return node; } -int imx_drm_encoder_parse_of(struct drm_device *drm, - struct drm_encoder *encoder, struct device_node *np) -{ - struct imx_drm_device *imxdrm = drm->dev_private; - struct device_node *ep = NULL; - uint32_t crtc_mask = 0; - int i; - - for (i = 0; ; i++) { - u32 mask; - - ep = imx_drm_of_get_next_endpoint(np, ep); - if (!ep) - break; - - mask = imx_drm_find_crtc_mask(imxdrm, ep); - - /* - * If we failed to find the CRTC(s) which this encoder is - * supposed to be connected to, it's because the CRTC has - * not been registered yet. Defer probing, and hope that - * the required CRTC is added later. - */ - if (mask == 0) - return -EPROBE_DEFER; - - crtc_mask |= mask; - } - - of_node_put(ep); - if (i == 0) - return -ENOENT; - - encoder->possible_crtcs = crtc_mask; - - /* FIXME: this is the mask of outputs which can clone this output. */ - encoder->possible_clones = ~0; - - return 0; -} -EXPORT_SYMBOL_GPL(imx_drm_encoder_parse_of); - /* * @node: device tree node containing encoder input ports * @encoder: drm_encoder @@ -510,7 +463,7 @@ int imx_drm_encoder_get_mux_id(struct device_node *node, port = of_graph_get_remote_port(ep); of_node_put(port); - if (port == imx_crtc->port) { + if (port == imx_crtc->crtc->port) { ret = of_graph_parse_endpoint(ep, &endpoint); return ret ? ret : endpoint.port; } diff --git a/drivers/gpu/drm/imx/imx-drm.h b/drivers/gpu/drm/imx/imx-drm.h index 7453ae0..3c559cc 100644 --- a/drivers/gpu/drm/imx/imx-drm.h +++ b/drivers/gpu/drm/imx/imx-drm.h @@ -17,7 +17,7 @@ int imx_drm_crtc_id(struct imx_drm_crtc *crtc); struct imx_drm_crtc_helper_funcs { int (*enable_vblank)(struct drm_crtc *crtc); void (*disable_vblank)(struct drm_crtc *crtc); - int (*set_interface_pix_fmt)(struct drm_crtc *crtc, u32 encoder_type, + int (*set_interface_pix_fmt)(struct drm_crtc *crtc, u32 pix_fmt, int hsync_pin, int vsync_pin); const struct drm_crtc_helper_funcs *crtc_helper_funcs; const struct drm_crtc_funcs *crtc_funcs; diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c index c604600..1b86aac 100644 --- a/drivers/gpu/drm/imx/imx-ldb.c +++ b/drivers/gpu/drm/imx/imx-ldb.c @@ -163,7 +163,7 @@ static void imx_ldb_encoder_prepare(struct drm_encoder *encoder) { struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); struct imx_ldb *ldb = imx_ldb_ch->ldb; - struct drm_display_mode *mode = &encoder->crtc->mode; + struct drm_display_mode *mode = &encoder->crtc->hwmode; u32 pixel_fmt; unsigned long serial_clk; unsigned long di_clk = mode->clock * 1000; @@ -241,8 +241,8 @@ static void imx_ldb_encoder_commit(struct drm_encoder *encoder) } static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) + struct drm_display_mode *orig_mode, + struct drm_display_mode *mode) { struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); struct imx_ldb *ldb = imx_ldb_ch->ldb; @@ -574,6 +574,8 @@ static void imx_ldb_unbind(struct device *dev, struct device *master, channel->connector.funcs->destroy(&channel->connector); channel->encoder.funcs->destroy(&channel->encoder); + + kfree(channel->edid); } } diff --git a/drivers/gpu/drm/imx/imx-tve.c b/drivers/gpu/drm/imx/imx-tve.c index a729f4f7..b63601d 100644 --- a/drivers/gpu/drm/imx/imx-tve.c +++ b/drivers/gpu/drm/imx/imx-tve.c @@ -307,8 +307,8 @@ static void imx_tve_encoder_prepare(struct drm_encoder *encoder) } static void imx_tve_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) + struct drm_display_mode *orig_mode, + struct drm_display_mode *mode) { struct imx_tve *tve = enc_to_tve(encoder); unsigned long rounded_rate; diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c index ebee59c..98551e3 100644 --- a/drivers/gpu/drm/imx/ipuv3-crtc.c +++ b/drivers/gpu/drm/imx/ipuv3-crtc.c @@ -46,7 +46,6 @@ struct ipu_crtc { struct drm_framebuffer *newfb; int irq; u32 interface_pix_fmt; - unsigned long di_clkflags; int di_hsync_pin; int di_vsync_pin; }; @@ -141,47 +140,51 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { + struct drm_device *dev = crtc->dev; + struct drm_encoder *encoder; struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); - int ret; struct ipu_di_signal_cfg sig_cfg = {}; + unsigned long encoder_types = 0; u32 out_pixel_fmt; + int ret; dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__, mode->hdisplay); dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__, mode->vdisplay); - out_pixel_fmt = ipu_crtc->interface_pix_fmt; + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) + if (encoder->crtc == crtc) + encoder_types |= BIT(encoder->encoder_type); - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - sig_cfg.interlaced = 1; - if (mode->flags & DRM_MODE_FLAG_PHSYNC) - sig_cfg.Hsync_pol = 1; - if (mode->flags & DRM_MODE_FLAG_PVSYNC) - sig_cfg.Vsync_pol = 1; + dev_dbg(ipu_crtc->dev, "%s: attached to encoder types 0x%lx\n", + __func__, encoder_types); + + /* + * If we have DAC, TVDAC or LDB, then we need the IPU DI clock + * to be the same as the LDB DI clock. + */ + if (encoder_types & (BIT(DRM_MODE_ENCODER_DAC) | + BIT(DRM_MODE_ENCODER_TVDAC) | + BIT(DRM_MODE_ENCODER_LVDS))) + sig_cfg.clkflags = IPU_DI_CLKMODE_SYNC | IPU_DI_CLKMODE_EXT; + else + sig_cfg.clkflags = 0; + + out_pixel_fmt = ipu_crtc->interface_pix_fmt; sig_cfg.enable_pol = 1; sig_cfg.clk_pol = 0; - sig_cfg.width = mode->hdisplay; - sig_cfg.height = mode->vdisplay; sig_cfg.pixel_fmt = out_pixel_fmt; - sig_cfg.h_start_width = mode->htotal - mode->hsync_end; - sig_cfg.h_sync_width = mode->hsync_end - mode->hsync_start; - sig_cfg.h_end_width = mode->hsync_start - mode->hdisplay; - - sig_cfg.v_start_width = mode->vtotal - mode->vsync_end; - sig_cfg.v_sync_width = mode->vsync_end - mode->vsync_start; - sig_cfg.v_end_width = mode->vsync_start - mode->vdisplay; - sig_cfg.pixelclock = mode->clock * 1000; - sig_cfg.clkflags = ipu_crtc->di_clkflags; - sig_cfg.v_to_h_sync = 0; - sig_cfg.hsync_pin = ipu_crtc->di_hsync_pin; sig_cfg.vsync_pin = ipu_crtc->di_vsync_pin; - ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di, sig_cfg.interlaced, - out_pixel_fmt, mode->hdisplay); + drm_display_mode_to_videomode(mode, &sig_cfg.mode); + + ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di, + mode->flags & DRM_MODE_FLAG_INTERLACE, + out_pixel_fmt, mode->hdisplay); if (ret) { dev_err(ipu_crtc->dev, "initializing display controller failed with %d\n", @@ -237,6 +240,18 @@ static bool ipu_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { + struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); + struct videomode vm; + int ret; + + drm_display_mode_to_videomode(adjusted_mode, &vm); + + ret = ipu_di_adjust_videomode(ipu_crtc->di, &vm); + if (ret) + return false; + + drm_display_mode_from_videomode(&vm, adjusted_mode); + return true; } @@ -275,7 +290,7 @@ static void ipu_disable_vblank(struct drm_crtc *crtc) ipu_crtc->newfb = NULL; } -static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, u32 encoder_type, +static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, u32 pixfmt, int hsync_pin, int vsync_pin) { struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); @@ -284,19 +299,6 @@ static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, u32 encoder_type, ipu_crtc->di_hsync_pin = hsync_pin; ipu_crtc->di_vsync_pin = vsync_pin; - switch (encoder_type) { - case DRM_MODE_ENCODER_DAC: - case DRM_MODE_ENCODER_TVDAC: - case DRM_MODE_ENCODER_LVDS: - ipu_crtc->di_clkflags = IPU_DI_CLKMODE_SYNC | - IPU_DI_CLKMODE_EXT; - break; - case DRM_MODE_ENCODER_TMDS: - case DRM_MODE_ENCODER_NONE: - ipu_crtc->di_clkflags = 0; - break; - } - return 0; } diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c index 796c3c1..5e83e00 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/parallel-display.c @@ -130,8 +130,8 @@ static void imx_pd_encoder_commit(struct drm_encoder *encoder) } static void imx_pd_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) + struct drm_display_mode *orig_mode, + struct drm_display_mode *mode) { } @@ -257,6 +257,8 @@ static void imx_pd_unbind(struct device *dev, struct device *master, imxpd->encoder.funcs->destroy(&imxpd->encoder); imxpd->connector.funcs->destroy(&imxpd->connector); + + kfree(imxpd->edid); } static const struct component_ops imx_pd_ops = { @@ -272,6 +274,7 @@ static int imx_pd_probe(struct platform_device *pdev) static int imx_pd_remove(struct platform_device *pdev) { component_del(&pdev->dev, &imx_pd_ops); + return 0; } diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig index ca9f085..0d87bf6 100644 --- a/drivers/gpu/drm/rockchip/Kconfig +++ b/drivers/gpu/drm/rockchip/Kconfig @@ -15,3 +15,13 @@ config DRM_ROCKCHIP management to userspace. This driver does not provide 2D or 3D acceleration; acceleration is performed by other IP found on the SoC. + +config ROCKCHIP_DW_HDMI + tristate "Rockchip specific extensions for Synopsys DW HDMI" + depends on DRM_ROCKCHIP + select DRM_DW_HDMI + help + This selects support for Rockchip SoC specific extensions + for the Synopsys DesignWare HDMI driver. If you want to + enable HDMI on RK3288 based SoC, you should selet this + option. diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index 2cb0672..f3d8a19 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -5,4 +5,6 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \ rockchip_drm_gem.o +obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o + obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c new file mode 100644 index 0000000..d236faa --- /dev/null +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <drm/drm_of.h> +#include <drm/drmP.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_encoder_slave.h> +#include <drm/bridge/dw_hdmi.h> + +#include "rockchip_drm_drv.h" +#include "rockchip_drm_vop.h" + +#define GRF_SOC_CON6 0x025c +#define HDMI_SEL_VOP_LIT (1 << 4) + +struct rockchip_hdmi { + struct device *dev; + struct regmap *regmap; + struct drm_encoder encoder; +}; + +#define to_rockchip_hdmi(x) container_of(x, struct rockchip_hdmi, x) + +static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = { + { + 27000000, { + { 0x00b3, 0x0000}, + { 0x2153, 0x0000}, + { 0x40f3, 0x0000} + }, + }, { + 36000000, { + { 0x00b3, 0x0000}, + { 0x2153, 0x0000}, + { 0x40f3, 0x0000} + }, + }, { + 40000000, { + { 0x00b3, 0x0000}, + { 0x2153, 0x0000}, + { 0x40f3, 0x0000} + }, + }, { + 54000000, { + { 0x0072, 0x0001}, + { 0x2142, 0x0001}, + { 0x40a2, 0x0001}, + }, + }, { + 65000000, { + { 0x0072, 0x0001}, + { 0x2142, 0x0001}, + { 0x40a2, 0x0001}, + }, + }, { + 66000000, { + { 0x013e, 0x0003}, + { 0x217e, 0x0002}, + { 0x4061, 0x0002} + }, + }, { + 74250000, { + { 0x0072, 0x0001}, + { 0x2145, 0x0002}, + { 0x4061, 0x0002} + }, + }, { + 83500000, { + { 0x0072, 0x0001}, + }, + }, { + 108000000, { + { 0x0051, 0x0002}, + { 0x2145, 0x0002}, + { 0x4061, 0x0002} + }, + }, { + 106500000, { + { 0x0051, 0x0002}, + { 0x2145, 0x0002}, + { 0x4061, 0x0002} + }, + }, { + 146250000, { + { 0x0051, 0x0002}, + { 0x2145, 0x0002}, + { 0x4061, 0x0002} + }, + }, { + 148500000, { + { 0x0051, 0x0003}, + { 0x214c, 0x0003}, + { 0x4064, 0x0003} + }, + }, { + ~0UL, { + { 0x00a0, 0x000a }, + { 0x2001, 0x000f }, + { 0x4002, 0x000f }, + }, + } +}; + +static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = { + /* pixelclk bpp8 bpp10 bpp12 */ + { + 40000000, { 0x0018, 0x0018, 0x0018 }, + }, { + 65000000, { 0x0028, 0x0028, 0x0028 }, + }, { + 66000000, { 0x0038, 0x0038, 0x0038 }, + }, { + 74250000, { 0x0028, 0x0038, 0x0038 }, + }, { + 83500000, { 0x0028, 0x0038, 0x0038 }, + }, { + 146250000, { 0x0038, 0x0038, 0x0038 }, + }, { + 148500000, { 0x0000, 0x0038, 0x0038 }, + }, { + ~0UL, { 0x0000, 0x0000, 0x0000}, + } +}; + +static const struct dw_hdmi_sym_term rockchip_sym_term[] = { + /*pixelclk symbol term*/ + { 74250000, 0x8009, 0x0004 }, + { 148500000, 0x8029, 0x0004 }, + { 297000000, 0x8039, 0x0005 }, + { ~0UL, 0x0000, 0x0000 } +}; + +static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) +{ + struct device_node *np = hdmi->dev->of_node; + + hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(hdmi->regmap)) { + dev_err(hdmi->dev, "Unable to get rockchip,grf\n"); + return PTR_ERR(hdmi->regmap); + } + + return 0; +} + +static enum drm_mode_status +dw_hdmi_rockchip_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg; + int pclk = mode->clock * 1000; + bool valid = false; + int i; + + for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) { + if (pclk == mpll_cfg[i].mpixelclock) { + valid = true; + break; + } + } + + return (valid) ? MODE_OK : MODE_BAD; +} + +static struct drm_encoder_funcs dw_hdmi_rockchip_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder) +{ +} + +static bool +dw_hdmi_rockchip_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ + return true; +} + +static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ +} + +static void dw_hdmi_rockchip_encoder_commit(struct drm_encoder *encoder) +{ + struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); + u32 val; + int mux; + + mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder); + if (mux) + val = HDMI_SEL_VOP_LIT | (HDMI_SEL_VOP_LIT << 16); + else + val = HDMI_SEL_VOP_LIT << 16; + + regmap_write(hdmi->regmap, GRF_SOC_CON6, val); + dev_dbg(hdmi->dev, "vop %s output to hdmi\n", + (mux) ? "LIT" : "BIG"); +} + +static void dw_hdmi_rockchip_encoder_prepare(struct drm_encoder *encoder) +{ + rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA, + ROCKCHIP_OUT_MODE_AAAA); +} + +static struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = { + .mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup, + .mode_set = dw_hdmi_rockchip_encoder_mode_set, + .prepare = dw_hdmi_rockchip_encoder_prepare, + .commit = dw_hdmi_rockchip_encoder_commit, + .disable = dw_hdmi_rockchip_encoder_disable, +}; + +static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = { + .mode_valid = dw_hdmi_rockchip_mode_valid, + .mpll_cfg = rockchip_mpll_cfg, + .cur_ctr = rockchip_cur_ctr, + .sym_term = rockchip_sym_term, + .dev_type = RK3288_HDMI, +}; + +static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { + { .compatible = "rockchip,rk3288-dw-hdmi", + .data = &rockchip_hdmi_drv_data + }, + {}, +}; +MODULE_DEVICE_TABLE(of, dw_hdmi_rockchip_dt_ids); + +static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, + void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + const struct dw_hdmi_plat_data *plat_data; + const struct of_device_id *match; + struct drm_device *drm = data; + struct drm_encoder *encoder; + struct rockchip_hdmi *hdmi; + struct resource *iores; + int irq; + int ret; + + if (!pdev->dev.of_node) + return -ENODEV; + + hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); + if (!hdmi) + return -ENOMEM; + + match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node); + plat_data = match->data; + hdmi->dev = &pdev->dev; + encoder = &hdmi->encoder; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iores) + return -ENXIO; + + platform_set_drvdata(pdev, hdmi); + + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); + /* + * If we failed to find the CRTC(s) which this encoder is + * supposed to be connected to, it's because the CRTC has + * not been registered yet. Defer probing, and hope that + * the required CRTC is added later. + */ + if (encoder->possible_crtcs == 0) + return -EPROBE_DEFER; + + ret = rockchip_hdmi_parse_dt(hdmi); + if (ret) { + dev_err(hdmi->dev, "Unable to parse OF data\n"); + return ret; + } + + drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs); + drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs, + DRM_MODE_ENCODER_TMDS); + + return dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data); +} + +static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master, + void *data) +{ + return dw_hdmi_unbind(dev, master, data); +} + +static const struct component_ops dw_hdmi_rockchip_ops = { + .bind = dw_hdmi_rockchip_bind, + .unbind = dw_hdmi_rockchip_unbind, +}; + +static int dw_hdmi_rockchip_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &dw_hdmi_rockchip_ops); +} + +static int dw_hdmi_rockchip_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dw_hdmi_rockchip_ops); + + return 0; +} + +static struct platform_driver dw_hdmi_rockchip_pltfm_driver = { + .probe = dw_hdmi_rockchip_probe, + .remove = dw_hdmi_rockchip_remove, + .driver = { + .name = "dwhdmi-rockchip", + .of_match_table = dw_hdmi_rockchip_dt_ids, + }, +}; + +module_platform_driver(dw_hdmi_rockchip_pltfm_driver); + +MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>"); +MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>"); +MODULE_DESCRIPTION("Rockchip Specific DW-HDMI Driver Extension"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:dwhdmi-rockchip"); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index a798c7c..21a481b 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -390,6 +390,7 @@ int rockchip_drm_encoder_get_mux_id(struct device_node *node, return -EINVAL; } +EXPORT_SYMBOL_GPL(rockchip_drm_encoder_get_mux_id); static int compare_of(struct device *dev, void *data) { diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index e7ca25b..9a5c571 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -735,6 +735,7 @@ int rockchip_drm_crtc_mode_config(struct drm_crtc *crtc, return 0; } +EXPORT_SYMBOL_GPL(rockchip_drm_crtc_mode_config); static int vop_crtc_enable_vblank(struct drm_crtc *crtc) { diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c index 2326c75..323203d 100644 --- a/drivers/gpu/ipu-v3/ipu-dc.c +++ b/drivers/gpu/ipu-v3/ipu-dc.c @@ -114,6 +114,7 @@ struct ipu_dc_priv { struct completion comp; int dc_irq; int dp_irq; + int use_count; }; static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority) @@ -232,7 +233,16 @@ EXPORT_SYMBOL_GPL(ipu_dc_init_sync); void ipu_dc_enable(struct ipu_soc *ipu) { - ipu_module_enable(ipu, IPU_CONF_DC_EN); + struct ipu_dc_priv *priv = ipu->dc_priv; + + mutex_lock(&priv->mutex); + + if (!priv->use_count) + ipu_module_enable(priv->ipu, IPU_CONF_DC_EN); + + priv->use_count++; + + mutex_unlock(&priv->mutex); } EXPORT_SYMBOL_GPL(ipu_dc_enable); @@ -294,7 +304,18 @@ EXPORT_SYMBOL_GPL(ipu_dc_disable_channel); void ipu_dc_disable(struct ipu_soc *ipu) { - ipu_module_disable(ipu, IPU_CONF_DC_EN); + struct ipu_dc_priv *priv = ipu->dc_priv; + + mutex_lock(&priv->mutex); + + priv->use_count--; + if (!priv->use_count) + ipu_module_disable(priv->ipu, IPU_CONF_DC_EN); + + if (priv->use_count < 0) + priv->use_count = 0; + + mutex_unlock(&priv->mutex); } EXPORT_SYMBOL_GPL(ipu_dc_disable); diff --git a/drivers/gpu/ipu-v3/ipu-di.c b/drivers/gpu/ipu-v3/ipu-di.c index c490ba4..b61d6be 100644 --- a/drivers/gpu/ipu-v3/ipu-di.c +++ b/drivers/gpu/ipu-v3/ipu-di.c @@ -207,10 +207,10 @@ static void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config, static void ipu_di_sync_config_interlaced(struct ipu_di *di, struct ipu_di_signal_cfg *sig) { - u32 h_total = sig->width + sig->h_sync_width + - sig->h_start_width + sig->h_end_width; - u32 v_total = sig->height + sig->v_sync_width + - sig->v_start_width + sig->v_end_width; + u32 h_total = sig->mode.hactive + sig->mode.hsync_len + + sig->mode.hback_porch + sig->mode.hfront_porch; + u32 v_total = sig->mode.vactive + sig->mode.vsync_len + + sig->mode.vback_porch + sig->mode.vfront_porch; u32 reg; struct di_sync_config cfg[] = { { @@ -229,13 +229,13 @@ static void ipu_di_sync_config_interlaced(struct ipu_di *di, }, { .run_count = v_total / 2 - 1, .run_src = DI_SYNC_HSYNC, - .offset_count = sig->v_start_width, + .offset_count = sig->mode.vback_porch, .offset_src = DI_SYNC_HSYNC, .repeat_count = 2, .cnt_clr_src = DI_SYNC_VSYNC, }, { .run_src = DI_SYNC_HSYNC, - .repeat_count = sig->height / 2, + .repeat_count = sig->mode.vactive / 2, .cnt_clr_src = 4, }, { .run_count = v_total - 1, @@ -249,9 +249,9 @@ static void ipu_di_sync_config_interlaced(struct ipu_di *di, .cnt_clr_src = DI_SYNC_VSYNC, }, { .run_src = DI_SYNC_CLK, - .offset_count = sig->h_start_width, + .offset_count = sig->mode.hback_porch, .offset_src = DI_SYNC_CLK, - .repeat_count = sig->width, + .repeat_count = sig->mode.hactive, .cnt_clr_src = 5, }, { .run_count = v_total - 1, @@ -277,10 +277,10 @@ static void ipu_di_sync_config_interlaced(struct ipu_di *di, static void ipu_di_sync_config_noninterlaced(struct ipu_di *di, struct ipu_di_signal_cfg *sig, int div) { - u32 h_total = sig->width + sig->h_sync_width + sig->h_start_width + - sig->h_end_width; - u32 v_total = sig->height + sig->v_sync_width + sig->v_start_width + - sig->v_end_width; + u32 h_total = sig->mode.hactive + sig->mode.hsync_len + + sig->mode.hback_porch + sig->mode.hfront_porch; + u32 v_total = sig->mode.vactive + sig->mode.vsync_len + + sig->mode.vback_porch + sig->mode.vfront_porch; struct di_sync_config cfg[] = { { /* 1: INT_HSYNC */ @@ -294,27 +294,29 @@ static void ipu_di_sync_config_noninterlaced(struct ipu_di *di, .offset_src = DI_SYNC_CLK, .cnt_polarity_gen_en = 1, .cnt_polarity_trigger_src = DI_SYNC_CLK, - .cnt_down = sig->h_sync_width * 2, + .cnt_down = sig->mode.hsync_len * 2, } , { /* PIN3: VSYNC */ .run_count = v_total - 1, .run_src = DI_SYNC_INT_HSYNC, .cnt_polarity_gen_en = 1, .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, - .cnt_down = sig->v_sync_width * 2, + .cnt_down = sig->mode.vsync_len * 2, } , { /* 4: Line Active */ .run_src = DI_SYNC_HSYNC, - .offset_count = sig->v_sync_width + sig->v_start_width, + .offset_count = sig->mode.vsync_len + + sig->mode.vback_porch, .offset_src = DI_SYNC_HSYNC, - .repeat_count = sig->height, + .repeat_count = sig->mode.vactive, .cnt_clr_src = DI_SYNC_VSYNC, } , { /* 5: Pixel Active, referenced by DC */ .run_src = DI_SYNC_CLK, - .offset_count = sig->h_sync_width + sig->h_start_width, + .offset_count = sig->mode.hsync_len + + sig->mode.hback_porch, .offset_src = DI_SYNC_CLK, - .repeat_count = sig->width, + .repeat_count = sig->mode.hactive, .cnt_clr_src = 5, /* Line Active */ } , { /* unused */ @@ -339,9 +341,10 @@ static void ipu_di_sync_config_noninterlaced(struct ipu_di *di, } , { /* 3: Line Active */ .run_src = DI_SYNC_INT_HSYNC, - .offset_count = sig->v_sync_width + sig->v_start_width, + .offset_count = sig->mode.vsync_len + + sig->mode.vback_porch, .offset_src = DI_SYNC_INT_HSYNC, - .repeat_count = sig->height, + .repeat_count = sig->mode.vactive, .cnt_clr_src = 3 /* VSYNC */, } , { /* PIN4: HSYNC for VGA via TVEv2 on TQ MBa53 */ @@ -351,13 +354,14 @@ static void ipu_di_sync_config_noninterlaced(struct ipu_di *di, .offset_src = DI_SYNC_CLK, .cnt_polarity_gen_en = 1, .cnt_polarity_trigger_src = DI_SYNC_CLK, - .cnt_down = sig->h_sync_width * 2, + .cnt_down = sig->mode.hsync_len * 2, } , { /* 5: Pixel Active signal to DC */ .run_src = DI_SYNC_CLK, - .offset_count = sig->h_sync_width + sig->h_start_width, + .offset_count = sig->mode.hsync_len + + sig->mode.hback_porch, .offset_src = DI_SYNC_CLK, - .repeat_count = sig->width, + .repeat_count = sig->mode.hactive, .cnt_clr_src = 4, /* Line Active */ } , { /* PIN6: VSYNC for VGA via TVEv2 on TQ MBa53 */ @@ -367,7 +371,7 @@ static void ipu_di_sync_config_noninterlaced(struct ipu_di *di, .offset_src = DI_SYNC_INT_HSYNC, .cnt_polarity_gen_en = 1, .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, - .cnt_down = sig->v_sync_width * 2, + .cnt_down = sig->mode.vsync_len * 2, } , { /* PIN4: HSYNC for VGA via TVEv2 on i.MX53-QSB */ .run_count = h_total - 1, @@ -376,7 +380,7 @@ static void ipu_di_sync_config_noninterlaced(struct ipu_di *di, .offset_src = DI_SYNC_CLK, .cnt_polarity_gen_en = 1, .cnt_polarity_trigger_src = DI_SYNC_CLK, - .cnt_down = sig->h_sync_width * 2, + .cnt_down = sig->mode.hsync_len * 2, } , { /* PIN6: VSYNC for VGA via TVEv2 on i.MX53-QSB */ .run_count = v_total - 1, @@ -385,7 +389,7 @@ static void ipu_di_sync_config_noninterlaced(struct ipu_di *di, .offset_src = DI_SYNC_INT_HSYNC, .cnt_polarity_gen_en = 1, .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, - .cnt_down = sig->v_sync_width * 2, + .cnt_down = sig->mode.vsync_len * 2, } , { /* unused */ }, @@ -433,10 +437,10 @@ static void ipu_di_config_clock(struct ipu_di *di, unsigned long in_rate; unsigned div; - clk_set_rate(clk, sig->pixelclock); + clk_set_rate(clk, sig->mode.pixelclock); in_rate = clk_get_rate(clk); - div = (in_rate + sig->pixelclock / 2) / sig->pixelclock; + div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock); if (div == 0) div = 1; @@ -454,10 +458,10 @@ static void ipu_di_config_clock(struct ipu_di *di, unsigned div, error; clkrate = clk_get_rate(di->clk_ipu); - div = (clkrate + sig->pixelclock / 2) / sig->pixelclock; + div = DIV_ROUND_CLOSEST(clkrate, sig->mode.pixelclock); rate = clkrate / div; - error = rate / (sig->pixelclock / 1000); + error = rate / (sig->mode.pixelclock / 1000); dev_dbg(di->ipu->dev, " IPU clock can give %lu with divider %u, error %d.%u%%\n", rate, div, (signed)(error - 1000) / 10, error % 10); @@ -473,10 +477,10 @@ static void ipu_di_config_clock(struct ipu_di *di, clk = di->clk_di; - clk_set_rate(clk, sig->pixelclock); + clk_set_rate(clk, sig->mode.pixelclock); in_rate = clk_get_rate(clk); - div = (in_rate + sig->pixelclock / 2) / sig->pixelclock; + div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock); if (div == 0) div = 1; @@ -504,35 +508,58 @@ static void ipu_di_config_clock(struct ipu_di *di, ipu_di_write(di, val, DI_GENERAL); dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, %luHz\n", - sig->pixelclock, + sig->mode.pixelclock, clk_get_rate(di->clk_ipu), clk_get_rate(di->clk_di), clk == di->clk_di ? "DI" : "IPU", clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4)); } +/* + * This function is called to adjust a video mode to IPU restrictions. + * It is meant to be called from drm crtc mode_fixup() methods. + */ +int ipu_di_adjust_videomode(struct ipu_di *di, struct videomode *mode) +{ + u32 diff; + + if (mode->vfront_porch >= 2) + return 0; + + diff = 2 - mode->vfront_porch; + + if (mode->vback_porch >= diff) { + mode->vfront_porch = 2; + mode->vback_porch -= diff; + } else if (mode->vsync_len > diff) { + mode->vfront_porch = 2; + mode->vsync_len = mode->vsync_len - diff; + } else { + dev_warn(di->ipu->dev, "failed to adjust videomode\n"); + return -EINVAL; + } + + dev_warn(di->ipu->dev, "videomode adapted for IPU restrictions\n"); + return 0; +} +EXPORT_SYMBOL_GPL(ipu_di_adjust_videomode); + int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) { u32 reg; u32 di_gen, vsync_cnt; u32 div; - u32 h_total, v_total; dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n", - di->id, sig->width, sig->height); + di->id, sig->mode.hactive, sig->mode.vactive); - if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0)) + if ((sig->mode.vsync_len == 0) || (sig->mode.hsync_len == 0)) return -EINVAL; - h_total = sig->width + sig->h_sync_width + sig->h_start_width + - sig->h_end_width; - v_total = sig->height + sig->v_sync_width + sig->v_start_width + - sig->v_end_width; - dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n", clk_get_rate(di->clk_ipu), clk_get_rate(di->clk_di), - sig->pixelclock); + sig->mode.pixelclock); mutex_lock(&di_mutex); @@ -551,7 +578,7 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) di_gen = ipu_di_read(di, DI_GENERAL) & DI_GEN_DI_CLK_EXT; di_gen |= DI_GEN_DI_VSYNC_EXT; - if (sig->interlaced) { + if (sig->mode.flags & DISPLAY_FLAGS_INTERLACED) { ipu_di_sync_config_interlaced(di, sig); /* set y_sel = 1 */ @@ -561,9 +588,9 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) vsync_cnt = 7; - if (sig->Hsync_pol) + if (sig->mode.flags & DISPLAY_FLAGS_HSYNC_HIGH) di_gen |= DI_GEN_POLARITY_3; - if (sig->Vsync_pol) + if (sig->mode.flags & DISPLAY_FLAGS_VSYNC_HIGH) di_gen |= DI_GEN_POLARITY_2; } else { ipu_di_sync_config_noninterlaced(di, sig, div); @@ -577,7 +604,7 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) if (!(sig->hsync_pin == 2 && sig->vsync_pin == 3)) vsync_cnt = 6; - if (sig->Hsync_pol) { + if (sig->mode.flags & DISPLAY_FLAGS_HSYNC_HIGH) { if (sig->hsync_pin == 2) di_gen |= DI_GEN_POLARITY_2; else if (sig->hsync_pin == 4) @@ -585,7 +612,7 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) else if (sig->hsync_pin == 7) di_gen |= DI_GEN_POLARITY_7; } - if (sig->Vsync_pol) { + if (sig->mode.flags & DISPLAY_FLAGS_VSYNC_HIGH) { if (sig->vsync_pin == 3) di_gen |= DI_GEN_POLARITY_3; else if (sig->vsync_pin == 6) diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h new file mode 100644 index 0000000..5a4f490 --- /dev/null +++ b/include/drm/bridge/dw_hdmi.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __DW_HDMI__ +#define __DW_HDMI__ + +#include <drm/drmP.h> + +enum { + DW_HDMI_RES_8, + DW_HDMI_RES_10, + DW_HDMI_RES_12, + DW_HDMI_RES_MAX, +}; + +enum dw_hdmi_devtype { + IMX6Q_HDMI, + IMX6DL_HDMI, + RK3288_HDMI, +}; + +struct dw_hdmi_mpll_config { + unsigned long mpixelclock; + struct { + u16 cpce; + u16 gmp; + } res[DW_HDMI_RES_MAX]; +}; + +struct dw_hdmi_curr_ctrl { + unsigned long mpixelclock; + u16 curr[DW_HDMI_RES_MAX]; +}; + +struct dw_hdmi_sym_term { + unsigned long mpixelclock; + u16 sym_ctr; /*clock symbol and transmitter control*/ + u16 term; /*transmission termination value*/ +}; + +struct dw_hdmi_plat_data { + enum dw_hdmi_devtype dev_type; + const struct dw_hdmi_mpll_config *mpll_cfg; + const struct dw_hdmi_curr_ctrl *cur_ctr; + const struct dw_hdmi_sym_term *sym_term; + enum drm_mode_status (*mode_valid)(struct drm_connector *connector, + struct drm_display_mode *mode); +}; + +void dw_hdmi_unbind(struct device *dev, struct device *master, void *data); +int dw_hdmi_bind(struct device *dev, struct device *master, + void *data, struct drm_encoder *encoder, + struct resource *iores, int irq, + const struct dw_hdmi_plat_data *plat_data); +#endif /* __IMX_HDMI_H__ */ diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h index b162ccd..d92f6dd 100644 --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -200,6 +200,8 @@ struct drm_display_mode *drm_gtf_mode_complex(struct drm_device *dev, int GTF_K, int GTF_2J); void drm_display_mode_from_videomode(const struct videomode *vm, struct drm_display_mode *dmode); +void drm_display_mode_to_videomode(const struct drm_display_mode *dmode, + struct videomode *vm); int of_get_drm_display_mode(struct device_node *np, struct drm_display_mode *dmode, int index); diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index c74bf4a..73390c1 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -17,6 +17,7 @@ #include <linux/bitmap.h> #include <linux/fb.h> #include <media/v4l2-mediabus.h> +#include <video/videomode.h> struct ipu_soc; @@ -32,28 +33,15 @@ enum ipuv3_type { * Bitfield of Display Interface signal polarities. */ struct ipu_di_signal_cfg { - unsigned datamask_en:1; - unsigned interlaced:1; - unsigned odd_field_first:1; - unsigned clksel_en:1; - unsigned clkidle_en:1; unsigned data_pol:1; /* true = inverted */ unsigned clk_pol:1; /* true = rising edge */ unsigned enable_pol:1; - unsigned Hsync_pol:1; /* true = active high */ - unsigned Vsync_pol:1; - u16 width; - u16 height; + struct videomode mode; + u32 pixel_fmt; - u16 h_start_width; - u16 h_sync_width; - u16 h_end_width; - u16 v_start_width; - u16 v_sync_width; - u16 v_end_width; u32 v_to_h_sync; - unsigned long pixelclock; + #define IPU_DI_CLKMODE_SYNC (1 << 0) #define IPU_DI_CLKMODE_EXT (1 << 1) unsigned long clkflags; @@ -236,6 +224,7 @@ void ipu_di_put(struct ipu_di *); int ipu_di_disable(struct ipu_di *); int ipu_di_enable(struct ipu_di *); int ipu_di_get_num(struct ipu_di *); +int ipu_di_adjust_videomode(struct ipu_di *di, struct videomode *mode); int ipu_di_init_sync_panel(struct ipu_di *, struct ipu_di_signal_cfg *sig); /* |