Commit ef67a902 authored by Laurent Pinchart's avatar Laurent Pinchart

drm/rcar-du: Rework output routing support

Split the output routing specification between SoC-internal data,
specified in the rcar_du_device_info structure, and board data, passed
through platform data.

The DU has 5 possible outputs (DPAD0/1, LVDS0/1, TCON). SoC-internal
output routing data specify which output are valid, which CRTCs can be
connected to the valid outputs, and the type of in-SoC encoder for the
output.

Platform data then specifies external encoders and the output they are
connected to.
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
parent 38b62fb3
...@@ -129,14 +129,16 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) ...@@ -129,14 +129,16 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay); rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay);
} }
void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output) void rcar_du_crtc_route_output(struct drm_crtc *crtc,
enum rcar_du_output output)
{ {
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
struct rcar_du_device *rcdu = rcrtc->group->dev;
/* Store the route from the CRTC output to the DU output. The DU will be /* Store the route from the CRTC output to the DU output. The DU will be
* configured when starting the CRTC. * configured when starting the CRTC.
*/ */
rcrtc->outputs |= 1 << output; rcrtc->outputs |= BIT(output);
} }
void rcar_du_crtc_update_planes(struct drm_crtc *crtc) void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#define __RCAR_DU_CRTC_H__ #define __RCAR_DU_CRTC_H__
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/platform_data/rcar-du.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
...@@ -45,7 +46,8 @@ void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, ...@@ -45,7 +46,8 @@ void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc); void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc);
void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc); void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc);
void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output); void rcar_du_crtc_route_output(struct drm_crtc *crtc,
enum rcar_du_output output);
void rcar_du_crtc_update_planes(struct drm_crtc *crtc); void rcar_du_crtc_update_planes(struct drm_crtc *crtc);
#endif /* __RCAR_DU_CRTC_H__ */ #endif /* __RCAR_DU_CRTC_H__ */
...@@ -219,12 +219,42 @@ static int rcar_du_remove(struct platform_device *pdev) ...@@ -219,12 +219,42 @@ static int rcar_du_remove(struct platform_device *pdev)
static const struct rcar_du_device_info rcar_du_r8a7779_info = { static const struct rcar_du_device_info rcar_du_r8a7779_info = {
.features = 0, .features = 0,
.num_crtcs = 2, .num_crtcs = 2,
.routes = {
/* R8A7779 has two RGB outputs and one (currently unsupported)
* TCON output.
*/
[RCAR_DU_OUTPUT_DPAD0] = {
.possible_crtcs = BIT(0),
.encoder_type = DRM_MODE_ENCODER_NONE,
},
[RCAR_DU_OUTPUT_DPAD1] = {
.possible_crtcs = BIT(1) | BIT(0),
.encoder_type = DRM_MODE_ENCODER_NONE,
},
},
}; };
static const struct rcar_du_device_info rcar_du_r8a7790_info = { static const struct rcar_du_device_info rcar_du_r8a7790_info = {
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_ALIGN_128B .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_ALIGN_128B
| RCAR_DU_FEATURE_DEFR8, | RCAR_DU_FEATURE_DEFR8,
.num_crtcs = 3, .num_crtcs = 3,
.routes = {
/* R8A7790 has one RGB output, two LVDS outputs and one
* (currently unsupported) TCON output.
*/
[RCAR_DU_OUTPUT_DPAD0] = {
.possible_crtcs = BIT(2) | BIT(1) | BIT(0),
.encoder_type = DRM_MODE_ENCODER_NONE,
},
[RCAR_DU_OUTPUT_LVDS0] = {
.possible_crtcs = BIT(0),
.encoder_type = DRM_MODE_ENCODER_LVDS,
},
[RCAR_DU_OUTPUT_LVDS1] = {
.possible_crtcs = BIT(2) | BIT(1),
.encoder_type = DRM_MODE_ENCODER_LVDS,
},
},
}; };
static const struct platform_device_id rcar_du_id_table[] = { static const struct platform_device_id rcar_du_id_table[] = {
......
...@@ -29,14 +29,30 @@ struct rcar_du_device; ...@@ -29,14 +29,30 @@ struct rcar_du_device;
#define RCAR_DU_FEATURE_ALIGN_128B (1 << 1) /* Align pitches to 128 bytes */ #define RCAR_DU_FEATURE_ALIGN_128B (1 << 1) /* Align pitches to 128 bytes */
#define RCAR_DU_FEATURE_DEFR8 (1 << 2) /* Has DEFR8 register */ #define RCAR_DU_FEATURE_DEFR8 (1 << 2) /* Has DEFR8 register */
/*
* struct rcar_du_output_routing - Output routing specification
* @possible_crtcs: bitmask of possible CRTCs for the output
* @encoder_type: DRM type of the internal encoder associated with the output
*
* The DU has 5 possible outputs (DPAD0/1, LVDS0/1, TCON). Output routing data
* specify the valid SoC outputs, which CRTCs can drive the output, and the type
* of in-SoC encoder for the output.
*/
struct rcar_du_output_routing {
unsigned int possible_crtcs;
unsigned int encoder_type;
};
/* /*
* struct rcar_du_device_info - DU model-specific information * struct rcar_du_device_info - DU model-specific information
* @features: device features (RCAR_DU_FEATURE_*) * @features: device features (RCAR_DU_FEATURE_*)
* @num_crtcs: total number of CRTCs * @num_crtcs: total number of CRTCs
* @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*)
*/ */
struct rcar_du_device_info { struct rcar_du_device_info {
unsigned int features; unsigned int features;
unsigned int num_crtcs; unsigned int num_crtcs;
struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX];
}; };
struct rcar_du_device { struct rcar_du_device {
......
...@@ -115,10 +115,12 @@ static const struct drm_encoder_funcs encoder_funcs = { ...@@ -115,10 +115,12 @@ static const struct drm_encoder_funcs encoder_funcs = {
}; };
int rcar_du_encoder_init(struct rcar_du_device *rcdu, int rcar_du_encoder_init(struct rcar_du_device *rcdu,
enum rcar_du_encoder_type type, unsigned int output, enum rcar_du_encoder_type type,
enum rcar_du_output output,
const struct rcar_du_encoder_data *data) const struct rcar_du_encoder_data *data)
{ {
struct rcar_du_encoder *renc; struct rcar_du_encoder *renc;
unsigned int encoder_type;
int ret; int ret;
renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL); renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
...@@ -127,19 +129,33 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu, ...@@ -127,19 +129,33 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
renc->output = output; renc->output = output;
switch (type) {
case RCAR_DU_ENCODER_VGA:
encoder_type = DRM_MODE_ENCODER_DAC;
break;
case RCAR_DU_ENCODER_LVDS:
encoder_type = DRM_MODE_ENCODER_LVDS;
break;
case RCAR_DU_ENCODER_NONE:
default:
/* No external encoder, use the internal encoder type. */
encoder_type = rcdu->info->routes[output].encoder_type;
break;
}
ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs, ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs,
type); encoder_type);
if (ret < 0) if (ret < 0)
return ret; return ret;
drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs); drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
switch (type) { switch (encoder_type) {
case RCAR_DU_ENCODER_LVDS: case DRM_MODE_ENCODER_LVDS:
return rcar_du_lvds_connector_init(rcdu, renc, return rcar_du_lvds_connector_init(rcdu, renc,
&data->connector.lvds.panel); &data->connector.lvds.panel);
case RCAR_DU_ENCODER_VGA: case DRM_MODE_ENCODER_DAC:
return rcar_du_vga_connector_init(rcdu, renc); return rcar_du_vga_connector_init(rcdu, renc);
default: default:
......
...@@ -22,7 +22,7 @@ struct rcar_du_device; ...@@ -22,7 +22,7 @@ struct rcar_du_device;
struct rcar_du_encoder { struct rcar_du_encoder {
struct drm_encoder encoder; struct drm_encoder encoder;
unsigned int output; enum rcar_du_output output;
}; };
#define to_rcar_encoder(e) \ #define to_rcar_encoder(e) \
...@@ -40,7 +40,8 @@ struct drm_encoder * ...@@ -40,7 +40,8 @@ struct drm_encoder *
rcar_du_connector_best_encoder(struct drm_connector *connector); rcar_du_connector_best_encoder(struct drm_connector *connector);
int rcar_du_encoder_init(struct rcar_du_device *rcdu, int rcar_du_encoder_init(struct rcar_du_device *rcdu,
enum rcar_du_encoder_type type, unsigned int output, enum rcar_du_encoder_type type,
enum rcar_du_output output,
const struct rcar_du_encoder_data *data); const struct rcar_du_encoder_data *data);
#endif /* __RCAR_DU_ENCODER_H__ */ #endif /* __RCAR_DU_ENCODER_H__ */
...@@ -135,11 +135,11 @@ void rcar_du_group_set_routing(struct rcar_du_group *rgrp) ...@@ -135,11 +135,11 @@ void rcar_du_group_set_routing(struct rcar_du_group *rgrp)
dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK); dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK);
/* Set the DU1 pins sources. Select CRTC 0 if explicitly requested and /* Set the DPAD1 pins sources. Select CRTC 0 if explicitly requested and
* CRTC 1 in all other cases to avoid cloning CRTC 0 to DU0 and DU1 by * CRTC 1 in all other cases to avoid cloning CRTC 0 to DPAD0 and DPAD1
* default. * by default.
*/ */
if (crtc0->outputs & (1 << 1)) if (crtc0->outputs & BIT(RCAR_DU_OUTPUT_DPAD1))
dorcr |= DORCR_PG2D_DS1; dorcr |= DORCR_PG2D_DS1;
else else
dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2; dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2;
......
...@@ -220,11 +220,14 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) ...@@ -220,11 +220,14 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
for (i = 0; i < rcdu->pdata->num_encoders; ++i) { for (i = 0; i < rcdu->pdata->num_encoders; ++i) {
const struct rcar_du_encoder_data *pdata = const struct rcar_du_encoder_data *pdata =
&rcdu->pdata->encoders[i]; &rcdu->pdata->encoders[i];
const struct rcar_du_output_routing *route =
&rcdu->info->routes[pdata->output];
if (pdata->type == RCAR_DU_ENCODER_UNUSED) if (pdata->type == RCAR_DU_ENCODER_UNUSED)
continue; continue;
if (pdata->output >= rcdu->num_crtcs) { if (pdata->output >= RCAR_DU_OUTPUT_MAX ||
route->possible_crtcs == 0) {
dev_warn(rcdu->dev, dev_warn(rcdu->dev,
"encoder %u references unexisting output %u, skipping\n", "encoder %u references unexisting output %u, skipping\n",
i, pdata->output); i, pdata->output);
...@@ -234,15 +237,17 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) ...@@ -234,15 +237,17 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
rcar_du_encoder_init(rcdu, pdata->type, pdata->output, pdata); rcar_du_encoder_init(rcdu, pdata->type, pdata->output, pdata);
} }
/* Set the possible CRTCs and possible clones. All encoders can be /* Set the possible CRTCs and possible clones. There's always at least
* driven by the CRTC associated with the output they're connected to, * one way for all encoders to clone each other, set all bits in the
* as well as by CRTC 0. * possible clones field.
*/ */
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
struct rcar_du_encoder *renc = to_rcar_encoder(encoder); struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
const struct rcar_du_output_routing *route =
&rcdu->info->routes[renc->output];
encoder->possible_crtcs = (1 << 0) | (1 << renc->output); encoder->possible_crtcs = route->possible_crtcs;
encoder->possible_clones = 1 << 0; encoder->possible_clones = (1 << rcdu->pdata->num_encoders) - 1;
} }
/* Now that the CRTCs have been initialized register the planes. */ /* Now that the CRTCs have been initialized register the planes. */
......
...@@ -16,8 +16,18 @@ ...@@ -16,8 +16,18 @@
#include <drm/drm_mode.h> #include <drm/drm_mode.h>
enum rcar_du_output {
RCAR_DU_OUTPUT_DPAD0,
RCAR_DU_OUTPUT_DPAD1,
RCAR_DU_OUTPUT_LVDS0,
RCAR_DU_OUTPUT_LVDS1,
RCAR_DU_OUTPUT_TCON,
RCAR_DU_OUTPUT_MAX,
};
enum rcar_du_encoder_type { enum rcar_du_encoder_type {
RCAR_DU_ENCODER_UNUSED = 0, RCAR_DU_ENCODER_UNUSED = 0,
RCAR_DU_ENCODER_NONE,
RCAR_DU_ENCODER_VGA, RCAR_DU_ENCODER_VGA,
RCAR_DU_ENCODER_LVDS, RCAR_DU_ENCODER_LVDS,
}; };
...@@ -39,13 +49,16 @@ struct rcar_du_connector_vga_data { ...@@ -39,13 +49,16 @@ struct rcar_du_connector_vga_data {
/* /*
* struct rcar_du_encoder_data - Encoder platform data * struct rcar_du_encoder_data - Encoder platform data
* @type: the encoder type (RCAR_DU_ENCODER_*) * @type: the encoder type (RCAR_DU_ENCODER_*)
* @output: the DU output the connector is connected to * @output: the DU output the connector is connected to (RCAR_DU_OUTPUT_*)
* @connector.lvds: platform data for LVDS connectors * @connector.lvds: platform data for LVDS connectors
* @connector.vga: platform data for VGA connectors * @connector.vga: platform data for VGA connectors
*
* Encoder platform data describes an on-board encoder, its associated DU SoC
* output, and the connector.
*/ */
struct rcar_du_encoder_data { struct rcar_du_encoder_data {
enum rcar_du_encoder_type type; enum rcar_du_encoder_type type;
unsigned int output; enum rcar_du_output output;
union { union {
struct rcar_du_connector_lvds_data lvds; struct rcar_du_connector_lvds_data lvds;
......
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