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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJUr4dcAAoJEFDCiBxwnmDr/scQAIZhXd+UtgqxoUe/gEiDO5FE EaWMzhnVKqc+aWlz2N4g968WDrC/F5zlL66i9AnYdmW8m2FwQkLVkHivALdHzvsb B1QCoo/LySlXJ25sUIGiSXt2kqsqnWykXCyKE0DicovS3oH/krye8kXoNmKe4pZx munUrXKH2XRAdSg+d5MjIVodfGWVgCyTq2DY4W+YII+KfFc0eC9qWSLdaPFe+eED Kg1QwcwRh7EQ4Smb+LX4bWqguYl6+kCEYQdio8cNLp8iYyxILG0ZJ/Unhm7y2bus 3c13VAFeMud5j40t4Bfzq3LcnxeBDAzR/RyNheracr6OAu5Jv0liRTe3Y8JWgq6c JqZ1hjch210RniCrorKajBJ+KKnHHtzpmardxK5p1L8f93btmNTDwanqiYohV+GE +V8SoOzG0ek69CC+yFPxh2oV0wQ+BLu+Yg4CLFLA2uSN9pmdxpgYlulB4LqYC4qw UKU40kNdv/Vzt0+FTKkw1yDQW3XZ7ZfP/9P89H8MFE1DKqkJl7dTEk6ZNDT/fjVS qyhQMbk4V1HuKVlL0WASFwIkyqDTIx9mz0wp+UMH1kk1tFlq5F20IgBPWhLgQqmR XgOiRR1yZkaZVAGTtAgQ4e5eKc87chEmtTOp1zcT1K53HPyn2WE6sISeLYttn+/4 cxRr39xl6M97Nfi2JkJO =r9/P -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
Коммит
fc83975348
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
ccflags-y := -Iinclude/drm
|
||||
|
||||
obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
|
||||
obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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
|
||||
|
|
|
@ -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");
|
|
@ -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,75 +408,19 @@ 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;
|
||||
uint32_t crtc_mask = drm_of_find_possible_crtcs(drm, np);
|
||||
|
||||
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;
|
||||
/*
|
||||
* 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;
|
||||
|
||||
encoder->possible_crtcs = crtc_mask;
|
||||
|
||||
|
@ -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,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
|
@ -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)
|
||||
|
|
|
@ -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__ */
|
|
@ -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);
|
||||
|
||||
/*
|
||||
|
|
Загрузка…
Ссылка в новой задаче