summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/bridge/ti-sn65dsi83.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/bridge/ti-sn65dsi83.c')
-rw-r--r--drivers/gpu/drm/bridge/ti-sn65dsi83.c200
1 files changed, 113 insertions, 87 deletions
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
index 750f2172ef08..a32f70bc68ea 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
@@ -137,7 +137,6 @@ enum sn65dsi83_model {
struct sn65dsi83 {
struct drm_bridge bridge;
- struct drm_display_mode mode;
struct device *dev;
struct regmap *regmap;
struct device_node *host_node;
@@ -147,8 +146,6 @@ struct sn65dsi83 {
int dsi_lanes;
bool lvds_dual_link;
bool lvds_dual_link_even_odd_swap;
- bool lvds_format_24bpp;
- bool lvds_format_jeida;
};
static const struct regmap_range sn65dsi83_readable_ranges[] = {
@@ -291,7 +288,8 @@ err_dsi_attach:
return ret;
}
-static void sn65dsi83_pre_enable(struct drm_bridge *bridge)
+static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_bridge_state)
{
struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
@@ -306,7 +304,8 @@ static void sn65dsi83_pre_enable(struct drm_bridge *bridge)
usleep_range(1000, 1100);
}
-static u8 sn65dsi83_get_lvds_range(struct sn65dsi83 *ctx)
+static u8 sn65dsi83_get_lvds_range(struct sn65dsi83 *ctx,
+ const struct drm_display_mode *mode)
{
/*
* The encoding of the LVDS_CLK_RANGE is as follows:
@@ -322,7 +321,7 @@ static u8 sn65dsi83_get_lvds_range(struct sn65dsi83 *ctx)
* the clock to 25..154 MHz, the range calculation can be simplified
* as follows:
*/
- int mode_clock = ctx->mode.clock;
+ int mode_clock = mode->clock;
if (ctx->lvds_dual_link)
mode_clock /= 2;
@@ -330,7 +329,8 @@ static u8 sn65dsi83_get_lvds_range(struct sn65dsi83 *ctx)
return (mode_clock - 12500) / 25000;
}
-static u8 sn65dsi83_get_dsi_range(struct sn65dsi83 *ctx)
+static u8 sn65dsi83_get_dsi_range(struct sn65dsi83 *ctx,
+ const struct drm_display_mode *mode)
{
/*
* The encoding of the CHA_DSI_CLK_RANGE is as follows:
@@ -346,7 +346,7 @@ static u8 sn65dsi83_get_dsi_range(struct sn65dsi83 *ctx)
* DSI_CLK = mode clock * bpp / dsi_data_lanes / 2
* the 2 is there because the bus is DDR.
*/
- return DIV_ROUND_UP(clamp((unsigned int)ctx->mode.clock *
+ return DIV_ROUND_UP(clamp((unsigned int)mode->clock *
mipi_dsi_pixel_format_to_bpp(ctx->dsi->format) /
ctx->dsi_lanes / 2, 40000U, 500000U), 5000U);
}
@@ -364,23 +364,73 @@ static u8 sn65dsi83_get_dsi_div(struct sn65dsi83 *ctx)
return dsi_div - 1;
}
-static void sn65dsi83_enable(struct drm_bridge *bridge)
+static void sn65dsi83_atomic_enable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_bridge_state)
{
struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
+ struct drm_atomic_state *state = old_bridge_state->base.state;
+ const struct drm_bridge_state *bridge_state;
+ const struct drm_crtc_state *crtc_state;
+ const struct drm_display_mode *mode;
+ struct drm_connector *connector;
+ struct drm_crtc *crtc;
+ bool lvds_format_24bpp;
+ bool lvds_format_jeida;
unsigned int pval;
+ __le16 le16val;
u16 val;
int ret;
+ /* Get the LVDS format from the bridge state. */
+ bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
+
+ switch (bridge_state->output_bus_cfg.format) {
+ case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
+ lvds_format_24bpp = false;
+ lvds_format_jeida = true;
+ break;
+ case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
+ lvds_format_24bpp = true;
+ lvds_format_jeida = true;
+ break;
+ case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
+ lvds_format_24bpp = true;
+ lvds_format_jeida = false;
+ break;
+ default:
+ /*
+ * Some bridges still don't set the correct
+ * LVDS bus pixel format, use SPWG24 default
+ * format until those are fixed.
+ */
+ lvds_format_24bpp = true;
+ lvds_format_jeida = false;
+ dev_warn(ctx->dev,
+ "Unsupported LVDS bus format 0x%04x, please check output bridge driver. Falling back to SPWG24.\n",
+ bridge_state->output_bus_cfg.format);
+ break;
+ }
+
+ /*
+ * Retrieve the CRTC adjusted mode. This requires a little dance to go
+ * from the bridge to the encoder, to the connector and to the CRTC.
+ */
+ connector = drm_atomic_get_new_connector_for_encoder(state,
+ bridge->encoder);
+ crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ mode = &crtc_state->adjusted_mode;
+
/* Clear reset, disable PLL */
regmap_write(ctx->regmap, REG_RC_RESET, 0x00);
regmap_write(ctx->regmap, REG_RC_PLL_EN, 0x00);
/* Reference clock derived from DSI link clock. */
regmap_write(ctx->regmap, REG_RC_LVDS_PLL,
- REG_RC_LVDS_PLL_LVDS_CLK_RANGE(sn65dsi83_get_lvds_range(ctx)) |
+ REG_RC_LVDS_PLL_LVDS_CLK_RANGE(sn65dsi83_get_lvds_range(ctx, mode)) |
REG_RC_LVDS_PLL_HS_CLK_SRC_DPHY);
regmap_write(ctx->regmap, REG_DSI_CLK,
- REG_DSI_CLK_CHA_DSI_CLK_RANGE(sn65dsi83_get_dsi_range(ctx)));
+ REG_DSI_CLK_CHA_DSI_CLK_RANGE(sn65dsi83_get_dsi_range(ctx, mode)));
regmap_write(ctx->regmap, REG_RC_DSI_CLK,
REG_RC_DSI_CLK_DSI_CLK_DIVIDER(sn65dsi83_get_dsi_div(ctx)));
@@ -394,20 +444,20 @@ static void sn65dsi83_enable(struct drm_bridge *bridge)
regmap_write(ctx->regmap, REG_DSI_EQ, 0x00);
/* Set up sync signal polarity. */
- val = (ctx->mode.flags & DRM_MODE_FLAG_NHSYNC ?
+ val = (mode->flags & DRM_MODE_FLAG_NHSYNC ?
REG_LVDS_FMT_HS_NEG_POLARITY : 0) |
- (ctx->mode.flags & DRM_MODE_FLAG_NVSYNC ?
+ (mode->flags & DRM_MODE_FLAG_NVSYNC ?
REG_LVDS_FMT_VS_NEG_POLARITY : 0);
/* Set up bits-per-pixel, 18bpp or 24bpp. */
- if (ctx->lvds_format_24bpp) {
+ if (lvds_format_24bpp) {
val |= REG_LVDS_FMT_CHA_24BPP_MODE;
if (ctx->lvds_dual_link)
val |= REG_LVDS_FMT_CHB_24BPP_MODE;
}
/* Set up LVDS format, JEIDA/Format 1 or SPWG/Format 2 */
- if (ctx->lvds_format_jeida) {
+ if (lvds_format_jeida) {
val |= REG_LVDS_FMT_CHA_24BPP_FORMAT1;
if (ctx->lvds_dual_link)
val |= REG_LVDS_FMT_CHB_24BPP_FORMAT1;
@@ -426,29 +476,29 @@ static void sn65dsi83_enable(struct drm_bridge *bridge)
REG_LVDS_LANE_CHB_LVDS_TERM);
regmap_write(ctx->regmap, REG_LVDS_CM, 0x00);
- val = cpu_to_le16(ctx->mode.hdisplay);
+ le16val = cpu_to_le16(mode->hdisplay);
regmap_bulk_write(ctx->regmap, REG_VID_CHA_ACTIVE_LINE_LENGTH_LOW,
- &val, 2);
- val = cpu_to_le16(ctx->mode.vdisplay);
+ &le16val, 2);
+ le16val = cpu_to_le16(mode->vdisplay);
regmap_bulk_write(ctx->regmap, REG_VID_CHA_VERTICAL_DISPLAY_SIZE_LOW,
- &val, 2);
+ &le16val, 2);
/* 32 + 1 pixel clock to ensure proper operation */
- val = cpu_to_le16(32 + 1);
- regmap_bulk_write(ctx->regmap, REG_VID_CHA_SYNC_DELAY_LOW, &val, 2);
- val = cpu_to_le16(ctx->mode.hsync_end - ctx->mode.hsync_start);
+ le16val = cpu_to_le16(32 + 1);
+ regmap_bulk_write(ctx->regmap, REG_VID_CHA_SYNC_DELAY_LOW, &le16val, 2);
+ le16val = cpu_to_le16(mode->hsync_end - mode->hsync_start);
regmap_bulk_write(ctx->regmap, REG_VID_CHA_HSYNC_PULSE_WIDTH_LOW,
- &val, 2);
- val = cpu_to_le16(ctx->mode.vsync_end - ctx->mode.vsync_start);
+ &le16val, 2);
+ le16val = cpu_to_le16(mode->vsync_end - mode->vsync_start);
regmap_bulk_write(ctx->regmap, REG_VID_CHA_VSYNC_PULSE_WIDTH_LOW,
- &val, 2);
+ &le16val, 2);
regmap_write(ctx->regmap, REG_VID_CHA_HORIZONTAL_BACK_PORCH,
- ctx->mode.htotal - ctx->mode.hsync_end);
+ mode->htotal - mode->hsync_end);
regmap_write(ctx->regmap, REG_VID_CHA_VERTICAL_BACK_PORCH,
- ctx->mode.vtotal - ctx->mode.vsync_end);
+ mode->vtotal - mode->vsync_end);
regmap_write(ctx->regmap, REG_VID_CHA_HORIZONTAL_FRONT_PORCH,
- ctx->mode.hsync_start - ctx->mode.hdisplay);
+ mode->hsync_start - mode->hdisplay);
regmap_write(ctx->regmap, REG_VID_CHA_VERTICAL_FRONT_PORCH,
- ctx->mode.vsync_start - ctx->mode.vdisplay);
+ mode->vsync_start - mode->vdisplay);
regmap_write(ctx->regmap, REG_VID_CHA_TEST_PATTERN, 0x00);
/* Enable PLL */
@@ -472,7 +522,8 @@ static void sn65dsi83_enable(struct drm_bridge *bridge)
regmap_write(ctx->regmap, REG_IRQ_STAT, pval);
}
-static void sn65dsi83_disable(struct drm_bridge *bridge)
+static void sn65dsi83_atomic_disable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_bridge_state)
{
struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
@@ -481,7 +532,8 @@ static void sn65dsi83_disable(struct drm_bridge *bridge)
regmap_write(ctx->regmap, REG_RC_PLL_EN, 0x00);
}
-static void sn65dsi83_post_disable(struct drm_bridge *bridge)
+static void sn65dsi83_atomic_post_disable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_bridge_state)
{
struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
@@ -503,70 +555,44 @@ sn65dsi83_mode_valid(struct drm_bridge *bridge,
return MODE_OK;
}
-static void sn65dsi83_mode_set(struct drm_bridge *bridge,
- const struct drm_display_mode *mode,
- const struct drm_display_mode *adj)
-{
- struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
+#define MAX_INPUT_SEL_FORMATS 1
- ctx->mode = *adj;
-}
-
-static bool sn65dsi83_mode_fixup(struct drm_bridge *bridge,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adj)
+static u32 *
+sn65dsi83_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state,
+ u32 output_fmt,
+ unsigned int *num_input_fmts)
{
- struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
- u32 input_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
- struct drm_encoder *encoder = bridge->encoder;
- struct drm_device *ddev = encoder->dev;
- struct drm_connector *connector;
+ u32 *input_fmts;
- /* The DSI format is always RGB888_1X24 */
- list_for_each_entry(connector, &ddev->mode_config.connector_list, head) {
- switch (connector->display_info.bus_formats[0]) {
- case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
- ctx->lvds_format_24bpp = false;
- ctx->lvds_format_jeida = true;
- break;
- case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
- ctx->lvds_format_24bpp = true;
- ctx->lvds_format_jeida = true;
- break;
- case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
- ctx->lvds_format_24bpp = true;
- ctx->lvds_format_jeida = false;
- break;
- default:
- /*
- * Some bridges still don't set the correct
- * LVDS bus pixel format, use SPWG24 default
- * format until those are fixed.
- */
- ctx->lvds_format_24bpp = true;
- ctx->lvds_format_jeida = false;
- dev_warn(ctx->dev,
- "Unsupported LVDS bus format 0x%04x, please check output bridge driver. Falling back to SPWG24.\n",
- connector->display_info.bus_formats[0]);
- break;
- }
+ *num_input_fmts = 0;
- drm_display_info_set_bus_formats(&connector->display_info,
- &input_bus_format, 1);
- }
+ input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
+ GFP_KERNEL);
+ if (!input_fmts)
+ return NULL;
+
+ /* This is the DSI-end bus format */
+ input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
+ *num_input_fmts = 1;
- return true;
+ return input_fmts;
}
static const struct drm_bridge_funcs sn65dsi83_funcs = {
- .attach = sn65dsi83_attach,
- .pre_enable = sn65dsi83_pre_enable,
- .enable = sn65dsi83_enable,
- .disable = sn65dsi83_disable,
- .post_disable = sn65dsi83_post_disable,
- .mode_valid = sn65dsi83_mode_valid,
- .mode_set = sn65dsi83_mode_set,
- .mode_fixup = sn65dsi83_mode_fixup,
+ .attach = sn65dsi83_attach,
+ .atomic_pre_enable = sn65dsi83_atomic_pre_enable,
+ .atomic_enable = sn65dsi83_atomic_enable,
+ .atomic_disable = sn65dsi83_atomic_disable,
+ .atomic_post_disable = sn65dsi83_atomic_post_disable,
+ .mode_valid = sn65dsi83_mode_valid,
+
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_get_input_bus_fmts = sn65dsi83_atomic_get_input_bus_fmts,
};
static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model)