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 ...@@ -3,3 +3,8 @@ config DRM_PTN3460
depends on DRM depends on DRM
select DRM_KMS_HELPER select DRM_KMS_HELPER
---help--- ---help---
config DRM_DW_HDMI
tristate
depends on DRM
select DRM_KMS_HELPER
ccflags-y := -Iinclude/drm ccflags-y := -Iinclude/drm
obj-$(CONFIG_DRM_PTN3460) += ptn3460.o obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
...@@ -837,7 +837,8 @@ enum { ...@@ -837,7 +837,8 @@ enum {
HDMI_PHY_CONF0_PDZ_OFFSET = 7, HDMI_PHY_CONF0_PDZ_OFFSET = 7,
HDMI_PHY_CONF0_ENTMDS_MASK = 0x40, HDMI_PHY_CONF0_ENTMDS_MASK = 0x40,
HDMI_PHY_CONF0_ENTMDS_OFFSET = 6, 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_MASK = 0x10,
HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET = 4, HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET = 4,
HDMI_PHY_CONF0_GEN2_TXPWRON_MASK = 0x8, HDMI_PHY_CONF0_GEN2_TXPWRON_MASK = 0x8,
...@@ -1029,4 +1030,5 @@ enum { ...@@ -1029,4 +1030,5 @@ enum {
HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH = 0x2, HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH = 0x2,
HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW = 0x0, HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW = 0x0,
}; };
#endif /* __IMX_HDMI_H__ */ #endif /* __IMX_HDMI_H__ */
...@@ -615,6 +615,46 @@ void drm_display_mode_from_videomode(const struct videomode *vm, ...@@ -615,6 +615,46 @@ void drm_display_mode_from_videomode(const struct videomode *vm,
} }
EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode); 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 #ifdef CONFIG_OF
/** /**
* of_get_drm_display_mode - get a drm_display_mode from devicetree * of_get_drm_display_mode - get a drm_display_mode from devicetree
......
...@@ -49,6 +49,7 @@ config DRM_IMX_IPUV3 ...@@ -49,6 +49,7 @@ config DRM_IMX_IPUV3
config DRM_IMX_HDMI config DRM_IMX_HDMI
tristate "Freescale i.MX DRM HDMI" tristate "Freescale i.MX DRM HDMI"
select DRM_DW_HDMI
depends on DRM_IMX depends on DRM_IMX
help help
Choose this if you want to use HDMI on i.MX6. Choose this if you want to use HDMI on i.MX6.
...@@ -9,4 +9,4 @@ obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o ...@@ -9,4 +9,4 @@ obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o
imx-ipuv3-crtc-objs := ipuv3-crtc.o ipuv3-plane.o imx-ipuv3-crtc-objs := ipuv3-crtc.o ipuv3-plane.o
obj-$(CONFIG_DRM_IMX_IPUV3) += imx-ipuv3-crtc.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 @@ ...@@ -25,6 +25,7 @@
#include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_cma_helper.h>
#include <drm/drm_fb_cma_helper.h> #include <drm/drm_fb_cma_helper.h>
#include <drm/drm_plane_helper.h> #include <drm/drm_plane_helper.h>
#include <drm/drm_of.h>
#include "imx-drm.h" #include "imx-drm.h"
...@@ -46,7 +47,6 @@ struct imx_drm_crtc { ...@@ -46,7 +47,6 @@ struct imx_drm_crtc {
struct drm_crtc *crtc; struct drm_crtc *crtc;
int pipe; int pipe;
struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs; struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs;
struct device_node *port;
}; };
static int legacyfb_depth = 16; static int legacyfb_depth = 16;
...@@ -116,8 +116,7 @@ int imx_drm_panel_format_pins(struct drm_encoder *encoder, ...@@ -116,8 +116,7 @@ int imx_drm_panel_format_pins(struct drm_encoder *encoder,
helper = &imx_crtc->imx_drm_helper_funcs; helper = &imx_crtc->imx_drm_helper_funcs;
if (helper->set_interface_pix_fmt) if (helper->set_interface_pix_fmt)
return helper->set_interface_pix_fmt(encoder->crtc, return helper->set_interface_pix_fmt(encoder->crtc,
encoder->encoder_type, interface_pix_fmt, interface_pix_fmt, hsync_pin, vsync_pin);
hsync_pin, vsync_pin);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(imx_drm_panel_format_pins); 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, ...@@ -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->imx_drm_helper_funcs = *imx_drm_helper_funcs;
imx_drm_crtc->pipe = imxdrm->pipes++; imx_drm_crtc->pipe = imxdrm->pipes++;
imx_drm_crtc->port = port;
imx_drm_crtc->crtc = crtc; imx_drm_crtc->crtc = crtc;
crtc->port = port;
imxdrm->crtc[imx_drm_crtc->pipe] = imx_drm_crtc; imxdrm->crtc[imx_drm_crtc->pipe] = imx_drm_crtc;
*new_crtc = imx_drm_crtc; *new_crtc = imx_drm_crtc;
...@@ -408,33 +408,28 @@ int imx_drm_remove_crtc(struct imx_drm_crtc *imx_drm_crtc) ...@@ -408,33 +408,28 @@ int imx_drm_remove_crtc(struct imx_drm_crtc *imx_drm_crtc)
} }
EXPORT_SYMBOL_GPL(imx_drm_remove_crtc); EXPORT_SYMBOL_GPL(imx_drm_remove_crtc);
/* int imx_drm_encoder_parse_of(struct drm_device *drm,
* Find the DRM CRTC possible mask for the connected endpoint. struct drm_encoder *encoder, struct device_node *np)
*
* 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; uint32_t crtc_mask = drm_of_find_possible_crtcs(drm, np);
unsigned i;
port = of_graph_get_remote_port(endpoint); /*
if (!port) * If we failed to find the CRTC(s) which this encoder is
return 0; * supposed to be connected to, it's because the CRTC has
of_node_put(port); * not been registered yet. Defer probing, and hope that
* the required CRTC is added later.
*/
if (crtc_mask == 0)
return -EPROBE_DEFER;
for (i = 0; i < MAX_CRTC; i++) { encoder->possible_crtcs = crtc_mask;
struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[i];
if (imx_drm_crtc && imx_drm_crtc->port == port) /* FIXME: this is the mask of outputs which can clone this output. */
return drm_crtc_mask(imx_drm_crtc->crtc); encoder->possible_clones = ~0;
}
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(imx_drm_encoder_parse_of);
static struct device_node *imx_drm_of_get_next_endpoint( static struct device_node *imx_drm_of_get_next_endpoint(
const struct device_node *parent, struct device_node *prev) const struct device_node *parent, struct device_node *prev)
...@@ -445,48 +440,6 @@ static struct device_node *imx_drm_of_get_next_endpoint( ...@@ -445,48 +440,6 @@ static struct device_node *imx_drm_of_get_next_endpoint(
return node; return node;
} }
int imx_drm_encoder_parse_of(struct drm_device *drm,
struct drm_encoder *encoder, struct device_node *np)
{
struct imx_drm_device *imxdrm = drm->dev_private;
struct device_node *ep = NULL;
uint32_t crtc_mask = 0;
int i;
for (i = 0; ; i++) {
u32 mask;
ep = imx_drm_of_get_next_endpoint(np, ep);
if (!ep)
break;
mask = imx_drm_find_crtc_mask(imxdrm, ep);
/*
* If we failed to find the CRTC(s) which this encoder is
* supposed to be connected to, it's because the CRTC has
* not been registered yet. Defer probing, and hope that
* the required CRTC is added later.
*/
if (mask == 0)
return -EPROBE_DEFER;
crtc_mask |= mask;
}
of_node_put(ep);
if (i == 0)
return -ENOENT;
encoder->possible_crtcs = crtc_mask;
/* FIXME: this is the mask of outputs which can clone this output. */
encoder->possible_clones = ~0;
return 0;
}
EXPORT_SYMBOL_GPL(imx_drm_encoder_parse_of);
/* /*
* @node: device tree node containing encoder input ports * @node: device tree node containing encoder input ports
* @encoder: drm_encoder * @encoder: drm_encoder
...@@ -510,7 +463,7 @@ int imx_drm_encoder_get_mux_id(struct device_node *node, ...@@ -510,7 +463,7 @@ int imx_drm_encoder_get_mux_id(struct device_node *node,
port = of_graph_get_remote_port(ep); port = of_graph_get_remote_port(ep);
of_node_put(port); of_node_put(port);
if (port == imx_crtc->port) { if (port == imx_crtc->crtc->port) {
ret = of_graph_parse_endpoint(ep, &endpoint); ret = of_graph_parse_endpoint(ep, &endpoint);
return ret ? ret : endpoint.port; return ret ? ret : endpoint.port;
} }
......
...@@ -17,7 +17,7 @@ int imx_drm_crtc_id(struct imx_drm_crtc *crtc); ...@@ -17,7 +17,7 @@ int imx_drm_crtc_id(struct imx_drm_crtc *crtc);
struct imx_drm_crtc_helper_funcs { struct imx_drm_crtc_helper_funcs {
int (*enable_vblank)(struct drm_crtc *crtc); int (*enable_vblank)(struct drm_crtc *crtc);
void (*disable_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); u32 pix_fmt, int hsync_pin, int vsync_pin);
const struct drm_crtc_helper_funcs *crtc_helper_funcs; const struct drm_crtc_helper_funcs *crtc_helper_funcs;
const struct drm_crtc_funcs *crtc_funcs; const struct drm_crtc_funcs *crtc_funcs;
......
...@@ -163,7 +163,7 @@ static void imx_ldb_encoder_prepare(struct drm_encoder *encoder) ...@@ -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_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
struct imx_ldb *ldb = imx_ldb_ch->ldb; 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; u32 pixel_fmt;
unsigned long serial_clk; unsigned long serial_clk;
unsigned long di_clk = mode->clock * 1000; unsigned long di_clk = mode->clock * 1000;
...@@ -241,8 +241,8 @@ static void imx_ldb_encoder_commit(struct drm_encoder *encoder) ...@@ -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, static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode, struct drm_display_mode *orig_mode,
struct drm_display_mode *adjusted_mode) struct drm_display_mode *mode)
{ {
struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
struct imx_ldb *ldb = imx_ldb_ch->ldb; struct imx_ldb *ldb = imx_ldb_ch->ldb;
...@@ -574,6 +574,8 @@ static void imx_ldb_unbind(struct device *dev, struct device *master, ...@@ -574,6 +574,8 @@ static void imx_ldb_unbind(struct device *dev, struct device *master,
channel->connector.funcs->destroy(&channel->connector); channel->connector.funcs->destroy(&channel->connector);
channel->encoder.funcs->destroy(&channel->encoder); channel->encoder.funcs->destroy(&channel->encoder);
kfree(channel->edid);
} }
} }
......
...@@ -307,8 +307,8 @@ static void imx_tve_encoder_prepare(struct drm_encoder *encoder) ...@@ -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, static void imx_tve_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode, struct drm_display_mode *orig_mode,
struct drm_display_mode *adjusted_mode) struct drm_display_mode *mode)
{ {
struct imx_tve *tve = enc_to_tve(encoder); struct imx_tve *tve = enc_to_tve(encoder);
unsigned long rounded_rate; unsigned long rounded_rate;
......
...@@ -46,7 +46,6 @@ struct ipu_crtc { ...@@ -46,7 +46,6 @@ struct ipu_crtc {
struct drm_framebuffer *newfb; struct drm_framebuffer *newfb;
int irq; int irq;
u32 interface_pix_fmt; u32 interface_pix_fmt;
unsigned long di_clkflags;
int di_hsync_pin; int di_hsync_pin;
int di_vsync_pin; int di_vsync_pin;
}; };
...@@ -141,47 +140,51 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc, ...@@ -141,47 +140,51 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc,
int x, int y, int x, int y,
struct drm_framebuffer *old_fb) struct drm_framebuffer *old_fb)
{ {
struct drm_device *dev = crtc->dev;
struct drm_encoder *encoder;
struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
int ret;
struct ipu_di_signal_cfg sig_cfg = {}; struct ipu_di_signal_cfg sig_cfg = {};
unsigned long encoder_types = 0;
u32 out_pixel_fmt; u32 out_pixel_fmt;
int ret;
dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__, dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__,
mode->hdisplay); mode->hdisplay);
dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__, dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__,
mode->vdisplay); 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) dev_dbg(ipu_crtc->dev, "%s: attached to encoder types 0x%lx\n",
sig_cfg.interlaced = 1; __func__, encoder_types);
if (mode->flags & DRM_MODE_FLAG_PHSYNC)
sig_cfg.Hsync_pol = 1; /*
if (mode->flags & DRM_MODE_FLAG_PVSYNC) * If we have DAC, TVDAC or LDB, then we need the IPU DI clock
sig_cfg.Vsync_pol = 1; * 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.enable_pol = 1;
sig_cfg.clk_pol = 0; sig_cfg.clk_pol = 0;
sig_cfg.width = mode->hdisplay;
sig_cfg.height = mode->vdisplay;
sig_cfg.pixel_fmt = out_pixel_fmt; 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.v_to_h_sync = 0;
sig_cfg.hsync_pin = ipu_crtc->di_hsync_pin; sig_cfg.hsync_pin = ipu_crtc->di_hsync_pin;
sig_cfg.vsync_pin = ipu_crtc->di_vsync_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);
out_pixel_fmt, mode->hdisplay);
ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di,
mode->flags & DRM_MODE_FLAG_INTERLACE,
out_pixel_fmt, mode->hdisplay);
if (ret) { if (ret) {
dev_err(ipu_crtc->dev, dev_err(ipu_crtc->dev,
"initializing display controller failed with %d\n", "initializing display controller failed with %d\n",
...@@ -237,6 +240,18 @@ static bool ipu_crtc_mode_fixup(struct drm_crtc *crtc, ...@@ -237,6 +240,18 @@ static bool ipu_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode, const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_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; return true;
} }
...@@ -275,7 +290,7 @@ static void ipu_disable_vblank(struct drm_crtc *crtc) ...@@ -275,7 +290,7 @@ static void ipu_disable_vblank(struct drm_crtc *crtc)
ipu_crtc->newfb = NULL; 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) u32 pixfmt, int hsync_pin, int vsync_pin)
{ {
struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 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, ...@@ -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_hsync_pin = hsync_pin;
ipu_crtc->di_vsync_pin = vsync_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; return 0;
} }
......
...@@ -130,8 +130,8 @@ static void imx_pd_encoder_commit(struct drm_encoder *encoder) ...@@ -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, static void imx_pd_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode, struct drm_display_mode *orig_mode,
struct drm_display_mode *adjusted_mode) struct drm_display_mode *mode)
{ {
} }
...@@ -257,6 +257,8 @@ static void imx_pd_unbind(struct device *dev, struct device *master, ...@@ -257,6 +257,8 @@ static void imx_pd_unbind(struct device *dev, struct device *master,
imxpd->encoder.funcs->destroy(&imxpd->encoder); imxpd->encoder.funcs->destroy(&imxpd->encoder);
imxpd->connector.funcs->destroy(&imxpd->connector); imxpd->connector.funcs->destroy(&imxpd->connector);
kfree(imxpd->edid);
} }
static const struct component_ops imx_pd_ops = { static const struct component_ops imx_pd_ops = {
...@@ -272,6 +274,7 @@ static int imx_pd_probe(struct platform_device *pdev) ...@@ -272,6 +274,7 @@ static int imx_pd_probe(struct platform_device *pdev)
static int imx_pd_remove(struct platform_device *pdev) static int imx_pd_remove(struct platform_device *pdev)
{ {
component_del(&pdev->dev, &imx_pd_ops); component_del(&pdev->dev, &imx_pd_ops);
return 0; return 0;
} }
......
...@@ -15,3 +15,13 @@ config DRM_ROCKCHIP ...@@ -15,3 +15,13 @@ config DRM_ROCKCHIP
management to userspace. This driver does not provide management to userspace. This driver does not provide
2D or 3D acceleration; acceleration is performed by other 2D or 3D acceleration; acceleration is performed by other
IP found on the SoC. 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 @@ ...@@ -5,4 +5,6 @@
rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
rockchip_drm_gem.o rockchip_drm_gem.o
obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.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, ...@@ -390,6 +390,7 @@ int rockchip_drm_encoder_get_mux_id(struct device_node *node,
return -EINVAL; return -EINVAL;
} }
EXPORT_SYMBOL_GPL(rockchip_drm_encoder_get_mux_id);
static int compare_of(struct device *dev, void *data) static int compare_of(struct device *dev, void *data)
{ {
......
...@@ -735,6 +735,7 @@ int rockchip_drm_crtc_mode_config(struct drm_crtc *crtc, ...@@ -735,6 +735,7 @@ int rockchip_drm_crtc_mode_config(struct drm_crtc *crtc,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(rockchip_drm_crtc_mode_config);
static int vop_crtc_enable_vblank(struct drm_crtc *crtc) static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
{ {
......
...@@ -114,6 +114,7 @@ struct ipu_dc_priv { ...@@ -114,6 +114,7 @@ struct ipu_dc_priv {
struct completion comp; struct completion comp;
int dc_irq; int dc_irq;
int dp_irq; int dp_irq;
int use_count;
}; };
static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority) 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); ...@@ -232,7 +233,16 @@ EXPORT_SYMBOL_GPL(ipu_dc_init_sync);
void ipu_dc_enable(struct ipu_soc *ipu) 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); EXPORT_SYMBOL_GPL(ipu_dc_enable);
...@@ -294,7 +304,18 @@ EXPORT_SYMBOL_GPL(ipu_dc_disable_channel); ...@@ -294,7 +304,18 @@ EXPORT_SYMBOL_GPL(ipu_dc_disable_channel);
void ipu_dc_disable(struct ipu_soc *ipu) 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); EXPORT_SYMBOL_GPL(ipu_dc_disable);
......
This diff is collapsed.
/*
* 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, ...@@ -200,6 +200,8 @@ struct drm_display_mode *drm_gtf_mode_complex(struct drm_device *dev,
int GTF_K, int GTF_2J); int GTF_K, int GTF_2J);
void drm_display_mode_from_videomode(const struct videomode *vm, void drm_display_mode_from_videomode(const struct videomode *vm,
struct drm_display_mode *dmode); 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, int of_get_drm_display_mode(struct device_node *np,
struct drm_display_mode *dmode, struct drm_display_mode *dmode,
int index); int index);
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/bitmap.h> #include <linux/bitmap.h>
#include <linux/fb.h> #include <linux/fb.h>
#include <media/v4l2-mediabus.h> #include <media/v4l2-mediabus.h>
#include <video/videomode.h>
struct ipu_soc; struct ipu_soc;
...@@ -32,28 +33,15 @@ enum ipuv3_type { ...@@ -32,28 +33,15 @@ enum ipuv3_type {
* Bitfield of Display Interface signal polarities. * Bitfield of Display Interface signal polarities.
*/ */
struct ipu_di_signal_cfg { 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 data_pol:1; /* true = inverted */
unsigned clk_pol:1; /* true = rising edge */ unsigned clk_pol:1; /* true = rising edge */
unsigned enable_pol:1; unsigned enable_pol:1;
unsigned Hsync_pol:1; /* true = active high */
unsigned Vsync_pol:1;
u16 width; struct videomode mode;
u16 height;
u32 pixel_fmt; 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; u32 v_to_h_sync;
unsigned long pixelclock;
#define IPU_DI_CLKMODE_SYNC (1 << 0) #define IPU_DI_CLKMODE_SYNC (1 << 0)
#define IPU_DI_CLKMODE_EXT (1 << 1) #define IPU_DI_CLKMODE_EXT (1 << 1)
unsigned long clkflags; unsigned long clkflags;
...@@ -236,6 +224,7 @@ void ipu_di_put(struct ipu_di *); ...@@ -236,6 +224,7 @@ void ipu_di_put(struct ipu_di *);
int ipu_di_disable(struct ipu_di *); int ipu_di_disable(struct ipu_di *);
int ipu_di_enable(struct ipu_di *); int ipu_di_enable(struct ipu_di *);
int ipu_di_get_num(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); 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