Commit fc839753 authored by Dave Airlie's avatar Dave Airlie

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
  ...
parents b2eb0489 d50141d8
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>;
};
};
};
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>;
};
};
};
};
......@@ -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
ccflags-y := -Iinclude/drm
obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
......@@ -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,
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,7 +335,7 @@ 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,
static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
unsigned long pixel_clk)
{
unsigned int clk_n, clk_cts;
......@@ -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,50 +631,54 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
HDMI_VP_CONF);
}
static inline void hdmi_phy_test_clear(struct imx_hdmi *hdmi,
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,
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,
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,
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,
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,
static void __hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
unsigned char addr)
{
hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
......@@ -679,125 +692,71 @@ static void __hdmi_phy_i2c_write(struct imx_hdmi *hdmi, unsigned short data,
hdmi_phy_wait_i2c_done(hdmi, 1000);
}
static int hdmi_phy_i2c_write(struct imx_hdmi *hdmi, unsigned short data,
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);
......@@ -843,7 +802,7 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep,
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,14 +810,13 @@ 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",
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;
......@@ -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)
{
imx_hdmi_setup(hdmi, &hdmi->previous_mode);
dw_hdmi_setup(hdmi, &hdmi->previous_mode);
}
static void imx_hdmi_poweroff(struct imx_hdmi *hdmi)
static void dw_hdmi_poweroff(struct dw_hdmi *hdmi)
{
imx_hdmi_phy_disable(hdmi);
dw_hdmi_phy_disable(hdmi);
}
static enum drm_connector_status imx_hdmi_connector_detect(struct drm_connector
*connector, bool force)
static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
struct drm_display_mode *orig_mode,
struct drm_display_mode *mode)
{
struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi,
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)
{
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 dw_hdmi_bridge_destroy(struct drm_bridge *bridge)
{
drm_bridge_cleanup(bridge);
kfree(bridge);
}
static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
{
/* 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);
imx_hdmi_setup(hdmi, mode);
struct dw_hdmi *hdmi = container_of(connector,
struct dw_hdmi, connector);
enum drm_mode_status mode_status = MODE_OK;
/* 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 (hdmi->plat_data->mode_valid)
mode_status = hdmi->plat_data->mode_valid(connector, mode);
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 imx_hdmi_connector_helper_funcs = {
.get_modes = imx_hdmi_connector_get_modes,
.best_encoder = imx_hdmi_connector_best_encoder,
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 irqreturn_t imx_hdmi_hardirq(int irq, void *dev_id)
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 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);
imx_hdmi_poweroff(hdmi);
dw_hdmi_poweroff(hdmi);
}
drm_helper_hpd_irq_event(hdmi->connector.dev);
}
......@@ -1538,134 +1517,121 @@ 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;
}
......@@ -1679,6 +1645,12 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
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 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
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");
......@@ -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__ */
......@@ -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
......
......@@ -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.
......@@ -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
/* 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");
......@@ -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,59 +408,10 @@ 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)
{
struct device_node *port;
unsigned i;
port = of_graph_get_remote_port(endpoint);
if (!port)
return 0;
of_node_put(port);
for (i = 0; i < MAX_CRTC; i++) {
struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[i];
if (imx_drm_crtc && imx_drm_crtc->port == port)
return drm_crtc_mask(imx_drm_crtc->crtc);
}
return 0;
}
static struct device_node *imx_drm_of_get_next_endpoint(
const struct device_node *parent, struct device_node *prev)
{
struct device_node *node = of_graph_get_next_endpoint(parent, prev);
of_node_put(prev);
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);
uint32_t crtc_mask = drm_of_find_possible_crtcs(drm, np);
/*
* If we failed to find the CRTC(s) which this encoder is
......@@ -468,16 +419,9 @@ int imx_drm_encoder_parse_of(struct drm_device *drm,
* not been registered yet. Defer probing, and hope that
* the required CRTC is added later.
*/
if (mask == 0)
if (crtc_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. */
......@@ -487,6 +431,15 @@ int imx_drm_encoder_parse_of(struct drm_device *drm,
}
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)
{
struct device_node *node = of_graph_get_next_endpoint(parent, prev);
of_node_put(prev);
return node;
}
/*
* @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;
}
......
......@@ -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;
......
......@@ -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);
}
}
......
......@@ -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;
......
......@@ -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,46 +140,50 @@ 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,
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,
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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.
......@@ -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
/*
* 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");
......@@ -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)
{
......
......@@ -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)
{
......
......@@ -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);
......
......@@ -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)
......
/*
* 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__ */
......@@ -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);
......
......@@ -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);
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment