@@ -112,6 +112,25 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay);
}
+static void rcar_du_crtc_set_routing(struct rcar_du_crtc *rcrtc)
+{
+ struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+ u32 dorcr = rcar_du_read(rcdu, DORCR);
+
+ dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK);
+
+ /* Set the DU1 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
+ * default.
+ */
+ if (rcrtc->outputs & (1 << 1) && rcrtc->index == 0)
+ dorcr |= DORCR_PG2D_DS1;
+ else
+ dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2;
+
+ rcar_du_write(rcdu, DORCR, dorcr);
+}
+
static void __rcar_du_start_stop(struct rcar_du_device *rcdu, bool start)
{
rcar_du_write(rcdu, DSYSR,
@@ -142,6 +161,16 @@ static void rcar_du_start_stop(struct rcar_du_device *rcdu, bool start)
}
}
+void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output)
+{
+ struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+ /* Store the route from the CRTC output to the DU output. The DU will be
+ * configured when starting the CRTC.
+ */
+ rcrtc->outputs |= 1 << output;
+}
+
void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
{
struct rcar_du_device *rcdu = crtc->dev->dev_private;
@@ -213,13 +242,6 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
rcar_du_write(rcdu, rcrtc->index ? DS2PR : DS1PR, dspr);
}
-/*
- * rcar_du_crtc_start - Configure and start the LCDC
- * @rcrtc: the SH Mobile CRTC
- *
- * Configure and start the LCDC device. External devices (clocks, MERAM, panels,
- * ...) are not touched by this function.
- */
static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
{
struct drm_crtc *crtc = &rcrtc->crtc;
@@ -236,11 +258,9 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
rcar_du_crtc_write(rcrtc, DOOR, DOOR_RGB(0, 0, 0));
rcar_du_crtc_write(rcrtc, BPOR, BPOR_RGB(0, 0, 0));
- /* Configure output routing: enable both superposition processors */
- rcar_du_write(rcdu, DORCR, DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2 |
- DORCR_PG1D_DS1 | DORCR_DPRS);
-
+ /* Configure display timings and output routing */
rcar_du_crtc_set_display_timing(rcrtc);
+ rcar_du_crtc_set_routing(rcrtc);
mutex_lock(&rcdu->planes.lock);
rcrtc->plane->enabled = true;
@@ -397,6 +417,8 @@ static int rcar_du_crtc_mode_set(struct drm_crtc *crtc,
rcar_du_plane_compute_base(rcrtc->plane, crtc->fb);
+ rcrtc->outputs = 0;
+
return 0;
error:
@@ -30,6 +30,7 @@ struct rcar_du_crtc {
bool started;
struct drm_pending_vblank_event *event;
+ unsigned int outputs;
int dpms;
struct rcar_du_plane *plane;
@@ -43,6 +44,7 @@ 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_resume(struct rcar_du_crtc *rcrtc);
+void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output);
void rcar_du_crtc_update_planes(struct drm_crtc *crtc);
#endif /* __RCAR_DU_CRTC_H__ */
@@ -60,6 +60,11 @@ int rcar_du_get(struct rcar_du_device *rcdu)
rcar_du_write(rcdu, DEFR3, DEFR3_CODE | DEFR3_DEFE3);
rcar_du_write(rcdu, DEFR4, DEFR4_CODE);
+ /* Use DS1PR and DS2PR to configure planes priorities and connects the
+ * superposition 0 to DU0 pins. DU1 pins will be configured dynamically.
+ */
+ rcar_du_write(rcdu, DORCR, DORCR_PG1D_DS1 | DORCR_DPRS);
+
done:
rcdu->use_count++;
return 0;
@@ -119,6 +119,9 @@ void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
+ struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
+
+ rcar_du_crtc_route_output(encoder->crtc, renc->output);
}
void rcar_du_encoder_mode_commit(struct drm_encoder *encoder)
@@ -165,7 +168,8 @@ static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
int rcar_du_modeset_init(struct rcar_du_device *rcdu)
{
- const struct rcar_du_encoder_data *pdata;
+ struct drm_device *dev = rcdu->ddev;
+ struct drm_encoder *encoder;
unsigned int i;
int ret;
@@ -181,20 +185,30 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
if (ret < 0)
return ret;
- for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) {
+ for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i)
rcar_du_crtc_create(rcdu, i);
- pdata = &rcdu->pdata->encoders[i];
- if (pdata->encoder == RCAR_DU_ENCODER_UNUSED)
+ rcdu->used_crtcs = 0;
+ rcdu->num_crtcs = i;
+
+ for (i = 0; i < rcdu->pdata->num_encoders; ++i) {
+ const struct rcar_du_encoder_data *pdata =
+ &rcdu->pdata->encoders[i];
+
+ if (pdata->output >= ARRAY_SIZE(rcdu->crtcs)) {
+ dev_warn(rcdu->dev,
+ "encoder %u references unexisting output %u, skipping\n",
+ i, pdata->output);
continue;
+ }
switch (pdata->encoder) {
case RCAR_DU_ENCODER_VGA:
- rcar_du_vga_init(rcdu, &pdata->u.vga, i);
+ rcar_du_vga_init(rcdu, &pdata->u.vga, pdata->output);
break;
case RCAR_DU_ENCODER_LVDS:
- rcar_du_lvds_init(rcdu, &pdata->u.lvds, i);
+ rcar_du_lvds_init(rcdu, &pdata->u.lvds, pdata->output);
break;
default:
@@ -202,8 +216,16 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
}
}
- rcdu->used_crtcs = 0;
- rcdu->num_crtcs = i;
+ /* Set the possible CRTCs and possible clones. All encoders can be
+ * driven by the CRTC associated with the output they're connected to,
+ * as well as by CRTC 0.
+ */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
+
+ encoder->possible_crtcs = (1 << 0) | (1 << renc->output);
+ encoder->possible_clones = 1 << 0;
+ }
ret = rcar_du_plane_register(rcdu);
if (ret < 0)
@@ -30,6 +30,7 @@ struct rcar_du_format_info {
struct rcar_du_encoder {
struct drm_encoder encoder;
+ unsigned int output;
};
#define to_rcar_encoder(e) \
@@ -188,25 +188,23 @@ static const struct drm_encoder_funcs encoder_funcs = {
int rcar_du_lvds_init(struct rcar_du_device *rcdu,
const struct rcar_du_encoder_lvds_data *data,
- unsigned int crtc)
+ unsigned int output)
{
struct rcar_du_encoder *renc;
- struct drm_encoder *encoder;
int ret;
renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
if (renc == NULL)
return -ENOMEM;
- encoder = &renc->encoder;
- encoder->possible_crtcs = 1 << crtc;
+ renc->output = output;
- ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
+ ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs,
DRM_MODE_ENCODER_LVDS);
if (ret < 0)
return ret;
- drm_encoder_helper_add(encoder, &encoder_helper_funcs);
+ drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
return rcar_du_lvds_connector_init(rcdu, renc, &data->panel);
}
@@ -14,12 +14,11 @@
#ifndef __RCAR_DU_LVDS_H__
#define __RCAR_DU_LVDS_H__
-struct rcar_du_crtc;
struct rcar_du_device;
struct rcar_du_encoder_lvds_data;
int rcar_du_lvds_init(struct rcar_du_device *rcdu,
const struct rcar_du_encoder_lvds_data *data,
- unsigned int crtc);
+ unsigned int output);
#endif /* __RCAR_DU_LVDS_H__ */
@@ -408,11 +408,13 @@
#define DORCR_PG2D_DS2 (1 << 24)
#define DORCR_PG2D_FIX0 (2 << 24)
#define DORCR_PG2D_DOOR (3 << 24)
+#define DORCR_PG2D_MASK (3 << 24)
#define DORCR_DR1D (1 << 21)
#define DORCR_PG1D_DS1 (0 << 16)
#define DORCR_PG1D_DS2 (1 << 16)
#define DORCR_PG1D_FIX0 (2 << 16)
#define DORCR_PG1D_DOOR (3 << 16)
+#define DORCR_PG1D_MASK (3 << 16)
#define DORCR_RGPV (1 << 4)
#define DORCR_DPRS (1 << 0)
@@ -127,25 +127,23 @@ static const struct drm_encoder_funcs encoder_funcs = {
int rcar_du_vga_init(struct rcar_du_device *rcdu,
const struct rcar_du_encoder_vga_data *data,
- unsigned int crtc)
+ unsigned int output)
{
struct rcar_du_encoder *renc;
- struct drm_encoder *encoder;
int ret;
renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
if (renc == NULL)
return -ENOMEM;
- encoder = &renc->encoder;
- encoder->possible_crtcs = 1 << crtc;
+ renc->output = output;
- ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
+ ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs,
DRM_MODE_ENCODER_DAC);
if (ret < 0)
return ret;
- drm_encoder_helper_add(encoder, &encoder_helper_funcs);
+ drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
return rcar_du_vga_connector_init(rcdu, renc);
}
@@ -19,6 +19,6 @@ struct rcar_du_encoder_vga_data;
int rcar_du_vga_init(struct rcar_du_device *rcdu,
const struct rcar_du_encoder_vga_data *data,
- unsigned int crtc);
+ unsigned int output);
#endif /* __RCAR_DU_VGA_H__ */
@@ -38,6 +38,8 @@ struct rcar_du_encoder_vga_data {
struct rcar_du_encoder_data {
enum rcar_du_encoder_type encoder;
+ unsigned int output;
+
union {
struct rcar_du_encoder_lvds_data lvds;
struct rcar_du_encoder_vga_data vga;
@@ -45,7 +47,8 @@ struct rcar_du_encoder_data {
};
struct rcar_du_platform_data {
- struct rcar_du_encoder_data encoders[2];
+ struct rcar_du_encoder_data *encoders;
+ unsigned int num_encoders;
};
#endif /* __RCAR_DU_H__ */