Merge branch 'drm/next/du' of git://linuxtv.org/pinchartl/media into drm-next
rcar-du -next branch. * 'drm/next/du' of git://linuxtv.org/pinchartl/media: drm: rcar-du: Fix LVDS start sequence on Gen3 drm: rcar-du: Fix H/V sync signal polarity configuration drm: rcar-du: Fix display timing controller parameter drm: rcar-du: Fix dot clock routing configuration drm: rcar-du: Add R8A7796 support drm: rcar-du: Add R8A7792 support drm: rcar-du: Simplify and fix probe error handling drm: rcar-du: Fix crash in encoder failure error path drm: rcar-du: Remove memory allocation error message drm: rcar-du: Remove test for impossible error condition drm: rcar-du: Bring HDMI encoder comments in line with the driver drm: rcar-du: Constify node argument to rcar_du_lvds_connector_init() video: of: Constify node argument to display timing functions
This commit is contained in:
commit
1a3865d64a
11 changed files with 117 additions and 71 deletions
|
@ -6,9 +6,11 @@ Required Properties:
|
|||
- "renesas,du-r8a7779" for R8A7779 (R-Car H1) compatible DU
|
||||
- "renesas,du-r8a7790" for R8A7790 (R-Car H2) compatible DU
|
||||
- "renesas,du-r8a7791" for R8A7791 (R-Car M2-W) compatible DU
|
||||
- "renesas,du-r8a7792" for R8A7792 (R-Car V2H) compatible DU
|
||||
- "renesas,du-r8a7793" for R8A7793 (R-Car M2-N) compatible DU
|
||||
- "renesas,du-r8a7794" for R8A7794 (R-Car E2) compatible DU
|
||||
- "renesas,du-r8a7795" for R8A7795 (R-Car H3) compatible DU
|
||||
- "renesas,du-r8a7796" for R8A7796 (R-Car M3-W) compatible DU
|
||||
|
||||
- reg: A list of base address and length of each memory resource, one for
|
||||
each entry in the reg-names property.
|
||||
|
@ -25,10 +27,10 @@ Required Properties:
|
|||
- clock-names: Name of the clocks. This property is model-dependent.
|
||||
- R8A7779 uses a single functional clock. The clock doesn't need to be
|
||||
named.
|
||||
- R8A779[01345] use one functional clock per channel and one clock per LVDS
|
||||
encoder (if available). The functional clocks must be named "du.x" with
|
||||
"x" being the channel numerical index. The LVDS clocks must be named
|
||||
"lvds.x" with "x" being the LVDS encoder numerical index.
|
||||
- R8A779[0123456] use one functional clock per channel and one clock per
|
||||
LVDS encoder (if available). The functional clocks must be named "du.x"
|
||||
with "x" being the channel numerical index. The LVDS clocks must be
|
||||
named "lvds.x" with "x" being the LVDS encoder numerical index.
|
||||
- In addition to the functional and encoder clocks, all DU versions also
|
||||
support externally supplied pixel clocks. Those clocks are optional.
|
||||
When supplied they must be named "dclkin.x" with "x" being the input
|
||||
|
@ -47,9 +49,11 @@ corresponding to each DU output.
|
|||
R8A7779 (H1) DPAD 0 DPAD 1 - -
|
||||
R8A7790 (H2) DPAD LVDS 0 LVDS 1 -
|
||||
R8A7791 (M2-W) DPAD LVDS 0 - -
|
||||
R8A7792 (V2H) DPAD 0 DPAD 1 - -
|
||||
R8A7793 (M2-N) DPAD LVDS 0 - -
|
||||
R8A7794 (E2) DPAD 0 DPAD 1 - -
|
||||
R8A7795 (H3) DPAD HDMI 0 HDMI 1 LVDS
|
||||
R8A7796 (M3-W) DPAD HDMI LVDS -
|
||||
|
||||
|
||||
Example: R8A7790 (R-Car H2) DU
|
||||
|
|
|
@ -149,8 +149,8 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
|
|||
rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0);
|
||||
|
||||
/* Signal polarities */
|
||||
value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : DSMR_VSL)
|
||||
| ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : DSMR_HSL)
|
||||
value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? DSMR_VSL : 0)
|
||||
| ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? DSMR_HSL : 0)
|
||||
| DSMR_DIPM_DISP | DSMR_CSPM;
|
||||
rcar_du_crtc_write(rcrtc, DSMR, value);
|
||||
|
||||
|
@ -172,7 +172,7 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
|
|||
mode->crtc_vsync_start - 1);
|
||||
rcar_du_crtc_write(rcrtc, VCR, mode->crtc_vtotal - 1);
|
||||
|
||||
rcar_du_crtc_write(rcrtc, DESR, mode->htotal - mode->hsync_start);
|
||||
rcar_du_crtc_write(rcrtc, DESR, mode->htotal - mode->hsync_start - 1);
|
||||
rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay);
|
||||
}
|
||||
|
||||
|
|
|
@ -110,6 +110,27 @@ static const struct rcar_du_device_info rcar_du_r8a7791_info = {
|
|||
.num_lvds = 1,
|
||||
};
|
||||
|
||||
static const struct rcar_du_device_info rcar_du_r8a7792_info = {
|
||||
.gen = 2,
|
||||
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
|
||||
| RCAR_DU_FEATURE_EXT_CTRL_REGS,
|
||||
.num_crtcs = 2,
|
||||
.routes = {
|
||||
/* R8A7792 has two RGB outputs. */
|
||||
[RCAR_DU_OUTPUT_DPAD0] = {
|
||||
.possible_crtcs = BIT(0),
|
||||
.encoder_type = DRM_MODE_ENCODER_NONE,
|
||||
.port = 0,
|
||||
},
|
||||
[RCAR_DU_OUTPUT_DPAD1] = {
|
||||
.possible_crtcs = BIT(1),
|
||||
.encoder_type = DRM_MODE_ENCODER_NONE,
|
||||
.port = 1,
|
||||
},
|
||||
},
|
||||
.num_lvds = 0,
|
||||
};
|
||||
|
||||
static const struct rcar_du_device_info rcar_du_r8a7794_info = {
|
||||
.gen = 2,
|
||||
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
|
||||
|
@ -157,13 +178,39 @@ static const struct rcar_du_device_info rcar_du_r8a7795_info = {
|
|||
.num_lvds = 1,
|
||||
};
|
||||
|
||||
static const struct rcar_du_device_info rcar_du_r8a7796_info = {
|
||||
.gen = 3,
|
||||
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
|
||||
| RCAR_DU_FEATURE_EXT_CTRL_REGS
|
||||
| RCAR_DU_FEATURE_VSP1_SOURCE,
|
||||
.num_crtcs = 3,
|
||||
.routes = {
|
||||
/* R8A7796 has one RGB output, one LVDS output and one
|
||||
* (currently unsupported) HDMI output.
|
||||
*/
|
||||
[RCAR_DU_OUTPUT_DPAD0] = {
|
||||
.possible_crtcs = BIT(2),
|
||||
.encoder_type = DRM_MODE_ENCODER_NONE,
|
||||
.port = 0,
|
||||
},
|
||||
[RCAR_DU_OUTPUT_LVDS0] = {
|
||||
.possible_crtcs = BIT(0),
|
||||
.encoder_type = DRM_MODE_ENCODER_LVDS,
|
||||
.port = 2,
|
||||
},
|
||||
},
|
||||
.num_lvds = 1,
|
||||
};
|
||||
|
||||
static const struct of_device_id rcar_du_of_table[] = {
|
||||
{ .compatible = "renesas,du-r8a7779", .data = &rcar_du_r8a7779_info },
|
||||
{ .compatible = "renesas,du-r8a7790", .data = &rcar_du_r8a7790_info },
|
||||
{ .compatible = "renesas,du-r8a7791", .data = &rcar_du_r8a7791_info },
|
||||
{ .compatible = "renesas,du-r8a7792", .data = &rcar_du_r8a7792_info },
|
||||
{ .compatible = "renesas,du-r8a7793", .data = &rcar_du_r8a7791_info },
|
||||
{ .compatible = "renesas,du-r8a7794", .data = &rcar_du_r8a7794_info },
|
||||
{ .compatible = "renesas,du-r8a7795", .data = &rcar_du_r8a7795_info },
|
||||
{ .compatible = "renesas,du-r8a7796", .data = &rcar_du_r8a7796_info },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -283,7 +330,6 @@ static int rcar_du_remove(struct platform_device *pdev)
|
|||
|
||||
drm_kms_helper_poll_fini(ddev);
|
||||
drm_mode_config_cleanup(ddev);
|
||||
drm_vblank_cleanup(ddev);
|
||||
|
||||
drm_dev_unref(ddev);
|
||||
|
||||
|
@ -292,18 +338,12 @@ static int rcar_du_remove(struct platform_device *pdev)
|
|||
|
||||
static int rcar_du_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct rcar_du_device *rcdu;
|
||||
struct drm_device *ddev;
|
||||
struct resource *mem;
|
||||
int ret;
|
||||
|
||||
if (np == NULL) {
|
||||
dev_err(&pdev->dev, "no device tree node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Allocate and initialize the DRM and R-Car device structures. */
|
||||
/* Allocate and initialize the R-Car device structure. */
|
||||
rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
|
||||
if (rcdu == NULL)
|
||||
return -ENOMEM;
|
||||
|
@ -313,6 +353,15 @@ static int rcar_du_probe(struct platform_device *pdev)
|
|||
rcdu->dev = &pdev->dev;
|
||||
rcdu->info = of_match_device(rcar_du_of_table, rcdu->dev)->data;
|
||||
|
||||
platform_set_drvdata(pdev, rcdu);
|
||||
|
||||
/* I/O resources */
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(rcdu->mmio))
|
||||
return PTR_ERR(rcdu->mmio);
|
||||
|
||||
/* DRM/KMS objects */
|
||||
ddev = drm_dev_alloc(&rcar_du_driver, &pdev->dev);
|
||||
if (IS_ERR(ddev))
|
||||
return PTR_ERR(ddev);
|
||||
|
@ -320,24 +369,6 @@ static int rcar_du_probe(struct platform_device *pdev)
|
|||
rcdu->ddev = ddev;
|
||||
ddev->dev_private = rcdu;
|
||||
|
||||
platform_set_drvdata(pdev, rcdu);
|
||||
|
||||
/* I/O resources */
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(rcdu->mmio)) {
|
||||
ret = PTR_ERR(rcdu->mmio);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Initialize vertical blanking interrupts handling. Start with vblank
|
||||
* disabled for all CRTCs.
|
||||
*/
|
||||
ret = drm_vblank_init(ddev, (1 << rcdu->info->num_crtcs) - 1);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
/* DRM/KMS objects */
|
||||
ret = rcar_du_modeset_init(rcdu);
|
||||
if (ret < 0) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
|
|
|
@ -105,16 +105,20 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp)
|
|||
if (rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_EXT_CTRL_REGS)) {
|
||||
rcar_du_group_setup_defr8(rgrp);
|
||||
|
||||
/* Configure input dot clock routing. We currently hardcode the
|
||||
* configuration to routing DOTCLKINn to DUn.
|
||||
/*
|
||||
* Configure input dot clock routing. We currently hardcode the
|
||||
* configuration to routing DOTCLKINn to DUn. Register fields
|
||||
* depend on the DU generation, but the resulting value is 0 in
|
||||
* all cases.
|
||||
*
|
||||
* On Gen2 a single register in the first group controls dot
|
||||
* clock selection for all channels, while on Gen3 dot clocks
|
||||
* are setup through per-group registers, only available when
|
||||
* the group has two channels.
|
||||
*/
|
||||
rcar_du_group_write(rgrp, DIDSR, DIDSR_CODE |
|
||||
DIDSR_LCDS_DCLKIN(2) |
|
||||
DIDSR_LCDS_DCLKIN(1) |
|
||||
DIDSR_LCDS_DCLKIN(0) |
|
||||
DIDSR_PDCS_CLK(2, 0) |
|
||||
DIDSR_PDCS_CLK(1, 0) |
|
||||
DIDSR_PDCS_CLK(0, 0));
|
||||
if ((rcdu->info->gen < 3 && rgrp->index == 0) ||
|
||||
(rcdu->info->gen == 3 && rgrp->num_crtcs > 1))
|
||||
rcar_du_group_write(rgrp, DIDSR, DIDSR_CODE);
|
||||
}
|
||||
|
||||
if (rcdu->info->gen >= 3)
|
||||
|
|
|
@ -108,7 +108,7 @@ int rcar_du_hdmienc_init(struct rcar_du_device *rcdu,
|
|||
if (hdmienc == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Locate drm bridge from the hdmi encoder DT node */
|
||||
/* Locate the DRM bridge from the HDMI encoder DT node. */
|
||||
bridge = of_drm_find_bridge(np);
|
||||
if (!bridge)
|
||||
return -EPROBE_DEFER;
|
||||
|
@ -123,7 +123,7 @@ int rcar_du_hdmienc_init(struct rcar_du_device *rcdu,
|
|||
renc->hdmi = hdmienc;
|
||||
hdmienc->renc = renc;
|
||||
|
||||
/* Link drm_bridge to encoder */
|
||||
/* Link the bridge to the encoder. */
|
||||
bridge->encoder = encoder;
|
||||
encoder->bridge = bridge;
|
||||
|
||||
|
|
|
@ -454,13 +454,13 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
|
|||
}
|
||||
|
||||
ret = rcar_du_encoder_init(rcdu, enc_type, output, encoder, connector);
|
||||
of_node_put(encoder);
|
||||
of_node_put(connector);
|
||||
|
||||
if (ret && ret != -EPROBE_DEFER)
|
||||
dev_warn(rcdu->dev,
|
||||
"failed to initialize encoder %s (%d), skipping\n",
|
||||
encoder->full_name, ret);
|
||||
"failed to initialize encoder %s on output %u (%d), skipping\n",
|
||||
of_node_full_name(encoder), output, ret);
|
||||
|
||||
of_node_put(encoder);
|
||||
of_node_put(connector);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -568,6 +568,13 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Initialize vertical blanking interrupts handling. Start with vblank
|
||||
* disabled for all CRTCs.
|
||||
*/
|
||||
ret = drm_vblank_init(dev, (1 << rcdu->info->num_crtcs) - 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Initialize the groups. */
|
||||
num_groups = DIV_ROUND_UP(rcdu->num_crtcs, 2);
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ static const struct drm_connector_funcs connector_funcs = {
|
|||
|
||||
int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
|
||||
struct rcar_du_encoder *renc,
|
||||
/* TODO const */ struct device_node *np)
|
||||
const struct device_node *np)
|
||||
{
|
||||
struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc);
|
||||
struct rcar_du_lvds_connector *lvdscon;
|
||||
|
|
|
@ -19,6 +19,6 @@ struct rcar_du_encoder;
|
|||
|
||||
int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
|
||||
struct rcar_du_encoder *renc,
|
||||
struct device_node *np);
|
||||
const struct device_node *np);
|
||||
|
||||
#endif /* __RCAR_DU_LVDSCON_H__ */
|
||||
|
|
|
@ -104,7 +104,14 @@ static void rcar_du_lvdsenc_start_gen3(struct rcar_du_lvdsenc *lvds,
|
|||
|
||||
rcar_lvds_write(lvds, LVDPLLCR, pllcr);
|
||||
|
||||
/* Turn the PLL on, set it to LVDS normal mode, wait for the startup
|
||||
/* Turn all the channels on. */
|
||||
rcar_lvds_write(lvds, LVDCR1,
|
||||
LVDCR1_CHSTBY_GEN3(3) | LVDCR1_CHSTBY_GEN3(2) |
|
||||
LVDCR1_CHSTBY_GEN3(1) | LVDCR1_CHSTBY_GEN3(0) |
|
||||
LVDCR1_CLKSTBY_GEN3);
|
||||
|
||||
/*
|
||||
* Turn the PLL on, set it to LVDS normal mode, wait for the startup
|
||||
* delay and turn the output on.
|
||||
*/
|
||||
lvdcr0 = LVDCR0_PLLON;
|
||||
|
@ -117,12 +124,6 @@ static void rcar_du_lvdsenc_start_gen3(struct rcar_du_lvdsenc *lvds,
|
|||
|
||||
lvdcr0 |= LVDCR0_LVRES;
|
||||
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
|
||||
|
||||
/* Turn all the channels on. */
|
||||
rcar_lvds_write(lvds, LVDCR1,
|
||||
LVDCR1_CHSTBY_GEN3(3) | LVDCR1_CHSTBY_GEN3(2) |
|
||||
LVDCR1_CHSTBY_GEN3(1) | LVDCR1_CHSTBY_GEN3(0) |
|
||||
LVDCR1_CLKSTBY_GEN3);
|
||||
}
|
||||
|
||||
static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
|
||||
|
@ -241,10 +242,8 @@ int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu)
|
|||
|
||||
for (i = 0; i < rcdu->info->num_lvds; ++i) {
|
||||
lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
|
||||
if (lvds == NULL) {
|
||||
dev_err(&pdev->dev, "failed to allocate private data\n");
|
||||
if (lvds == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
lvds->dev = rcdu;
|
||||
lvds->index = i;
|
||||
|
|
|
@ -119,7 +119,7 @@ static int of_parse_display_timing(const struct device_node *np,
|
|||
* @name: name of the timing node
|
||||
* @dt: display_timing struct to fill
|
||||
**/
|
||||
int of_get_display_timing(struct device_node *np, const char *name,
|
||||
int of_get_display_timing(const struct device_node *np, const char *name,
|
||||
struct display_timing *dt)
|
||||
{
|
||||
struct device_node *timing_np;
|
||||
|
@ -142,7 +142,7 @@ EXPORT_SYMBOL_GPL(of_get_display_timing);
|
|||
* of_get_display_timings - parse all display_timing entries from a device_node
|
||||
* @np: device_node with the subnodes
|
||||
**/
|
||||
struct display_timings *of_get_display_timings(struct device_node *np)
|
||||
struct display_timings *of_get_display_timings(const struct device_node *np)
|
||||
{
|
||||
struct device_node *timings_np;
|
||||
struct device_node *entry;
|
||||
|
@ -258,7 +258,7 @@ EXPORT_SYMBOL_GPL(of_get_display_timings);
|
|||
* of_display_timings_exist - check if a display-timings node is provided
|
||||
* @np: device_node with the timing
|
||||
**/
|
||||
int of_display_timings_exist(struct device_node *np)
|
||||
int of_display_timings_exist(const struct device_node *np)
|
||||
{
|
||||
struct device_node *timings_np;
|
||||
|
||||
|
|
|
@ -16,21 +16,22 @@ struct display_timings;
|
|||
#define OF_USE_NATIVE_MODE -1
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
int of_get_display_timing(struct device_node *np, const char *name,
|
||||
int of_get_display_timing(const struct device_node *np, const char *name,
|
||||
struct display_timing *dt);
|
||||
struct display_timings *of_get_display_timings(struct device_node *np);
|
||||
int of_display_timings_exist(struct device_node *np);
|
||||
struct display_timings *of_get_display_timings(const struct device_node *np);
|
||||
int of_display_timings_exist(const struct device_node *np);
|
||||
#else
|
||||
static inline int of_get_display_timing(struct device_node *np, const char *name,
|
||||
struct display_timing *dt)
|
||||
static inline int of_get_display_timing(const struct device_node *np,
|
||||
const char *name, struct display_timing *dt)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
static inline struct display_timings *of_get_display_timings(struct device_node *np)
|
||||
static inline struct display_timings *
|
||||
of_get_display_timings(const struct device_node *np)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline int of_display_timings_exist(struct device_node *np)
|
||||
static inline int of_display_timings_exist(const struct device_node *np)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue