diff options
Diffstat (limited to 'drivers/gpu/drm/bridge/ti-sn65dsi83.c')
-rw-r--r-- | drivers/gpu/drm/bridge/ti-sn65dsi83.c | 200 |
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) |