diff options
Diffstat (limited to 'drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c')
| -rw-r--r-- | drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c | 151 |
1 files changed, 114 insertions, 37 deletions
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c index 50ec109aa6ed..f87337c3cbb5 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c @@ -11,6 +11,7 @@ #include <linux/dma-mapping.h> #include <linux/io.h> #include <linux/iopoll.h> +#include <linux/math.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_graph.h> @@ -18,6 +19,7 @@ #include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/slab.h> +#include <linux/units.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> @@ -32,10 +34,31 @@ #define RZG2L_DCS_BUF_SIZE 128 /* Maximum DCS buffer size in external memory. */ +#define RZ_MIPI_DSI_FEATURE_16BPP BIT(0) + +struct rzg2l_mipi_dsi; + +struct rzg2l_mipi_dsi_hw_info { + int (*dphy_init)(struct rzg2l_mipi_dsi *dsi, u64 hsfreq_millihz); + void (*dphy_startup_late_init)(struct rzg2l_mipi_dsi *dsi); + void (*dphy_exit)(struct rzg2l_mipi_dsi *dsi); + int (*dphy_conf_clks)(struct rzg2l_mipi_dsi *dsi, unsigned long mode_freq, + u64 *hsfreq_millihz); + unsigned int (*dphy_mode_clk_check)(struct rzg2l_mipi_dsi *dsi, + unsigned long mode_freq); + u32 phy_reg_offset; + u32 link_reg_offset; + unsigned long min_dclk; + unsigned long max_dclk; + u8 features; +}; + struct rzg2l_mipi_dsi { struct device *dev; void __iomem *mmio; + const struct rzg2l_mipi_dsi_hw_info *info; + struct reset_control *rstc; struct reset_control *arstc; struct reset_control *prstc; @@ -85,7 +108,7 @@ struct rzg2l_mipi_dsi_timings { static const struct rzg2l_mipi_dsi_timings rzg2l_mipi_dsi_global_timings[] = { { - .hsfreq_max = 80000, + .hsfreq_max = 80000000, .t_init = 79801, .tclk_prepare = 8, .ths_prepare = 13, @@ -99,7 +122,7 @@ static const struct rzg2l_mipi_dsi_timings rzg2l_mipi_dsi_global_timings[] = { .tlpx = 6, }, { - .hsfreq_max = 125000, + .hsfreq_max = 125000000, .t_init = 79801, .tclk_prepare = 8, .ths_prepare = 12, @@ -113,7 +136,7 @@ static const struct rzg2l_mipi_dsi_timings rzg2l_mipi_dsi_global_timings[] = { .tlpx = 6, }, { - .hsfreq_max = 250000, + .hsfreq_max = 250000000, .t_init = 79801, .tclk_prepare = 8, .ths_prepare = 12, @@ -127,7 +150,7 @@ static const struct rzg2l_mipi_dsi_timings rzg2l_mipi_dsi_global_timings[] = { .tlpx = 6, }, { - .hsfreq_max = 360000, + .hsfreq_max = 360000000, .t_init = 79801, .tclk_prepare = 8, .ths_prepare = 10, @@ -141,7 +164,7 @@ static const struct rzg2l_mipi_dsi_timings rzg2l_mipi_dsi_global_timings[] = { .tlpx = 6, }, { - .hsfreq_max = 720000, + .hsfreq_max = 720000000, .t_init = 79801, .tclk_prepare = 8, .ths_prepare = 9, @@ -155,7 +178,7 @@ static const struct rzg2l_mipi_dsi_timings rzg2l_mipi_dsi_global_timings[] = { .tlpx = 6, }, { - .hsfreq_max = 1500000, + .hsfreq_max = 1500000000, .t_init = 79801, .tclk_prepare = 8, .ths_prepare = 9, @@ -172,22 +195,22 @@ static const struct rzg2l_mipi_dsi_timings rzg2l_mipi_dsi_global_timings[] = { static void rzg2l_mipi_dsi_phy_write(struct rzg2l_mipi_dsi *dsi, u32 reg, u32 data) { - iowrite32(data, dsi->mmio + reg); + iowrite32(data, dsi->mmio + dsi->info->phy_reg_offset + reg); } static void rzg2l_mipi_dsi_link_write(struct rzg2l_mipi_dsi *dsi, u32 reg, u32 data) { - iowrite32(data, dsi->mmio + LINK_REG_OFFSET + reg); + iowrite32(data, dsi->mmio + dsi->info->link_reg_offset + reg); } static u32 rzg2l_mipi_dsi_phy_read(struct rzg2l_mipi_dsi *dsi, u32 reg) { - return ioread32(dsi->mmio + reg); + return ioread32(dsi->mmio + dsi->info->phy_reg_offset + reg); } static u32 rzg2l_mipi_dsi_link_read(struct rzg2l_mipi_dsi *dsi, u32 reg) { - return ioread32(dsi->mmio + LINK_REG_OFFSET + reg); + return ioread32(dsi->mmio + dsi->info->link_reg_offset + reg); } /* ----------------------------------------------------------------------------- @@ -195,8 +218,9 @@ static u32 rzg2l_mipi_dsi_link_read(struct rzg2l_mipi_dsi *dsi, u32 reg) */ static int rzg2l_mipi_dsi_dphy_init(struct rzg2l_mipi_dsi *dsi, - unsigned long hsfreq) + u64 hsfreq_millihz) { + unsigned long hsfreq = DIV_ROUND_CLOSEST_ULL(hsfreq_millihz, MILLI); const struct rzg2l_mipi_dsi_timings *dphy_timings; unsigned int i; u32 dphyctrl0; @@ -265,21 +289,17 @@ static void rzg2l_mipi_dsi_dphy_exit(struct rzg2l_mipi_dsi *dsi) reset_control_assert(dsi->rstc); } -static int rzg2l_mipi_dsi_startup(struct rzg2l_mipi_dsi *dsi, - const struct drm_display_mode *mode) +static int rzg2l_dphy_conf_clks(struct rzg2l_mipi_dsi *dsi, unsigned long mode_freq, + u64 *hsfreq_millihz) { - unsigned long hsfreq; + unsigned long vclk_rate; unsigned int bpp; - u32 txsetr; - u32 clstptsetr; - u32 lptrnstsetr; - u32 clkkpt; - u32 clkbfht; - u32 clkstpt; - u32 golpbkt; - u32 dsisetr; - int ret; + clk_set_rate(dsi->vclk, mode_freq * KILO); + vclk_rate = clk_get_rate(dsi->vclk); + if (vclk_rate != mode_freq * KILO) + dev_dbg(dsi->dev, "Requested vclk rate %lu, actual %lu mismatch\n", + mode_freq * KILO, vclk_rate); /* * Relationship between hsclk and vclk must follow * vclk * bpp = hsclk * 8 * lanes @@ -288,18 +308,39 @@ static int rzg2l_mipi_dsi_startup(struct rzg2l_mipi_dsi *dsi, * hsclk: DSI HS Byte clock frequency (Hz) * lanes: number of data lanes * - * hsclk(bit) = hsclk(byte) * 8 + * hsclk(bit) = hsclk(byte) * 8 = hsfreq */ bpp = mipi_dsi_pixel_format_to_bpp(dsi->format); - hsfreq = (mode->clock * bpp * 8) / (8 * dsi->lanes); + *hsfreq_millihz = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(vclk_rate, bpp * MILLI), + dsi->lanes); + + return 0; +} + +static int rzg2l_mipi_dsi_startup(struct rzg2l_mipi_dsi *dsi, + const struct drm_display_mode *mode) +{ + unsigned long hsfreq; + u64 hsfreq_millihz; + u32 txsetr; + u32 clstptsetr; + u32 lptrnstsetr; + u32 clkkpt; + u32 clkbfht; + u32 clkstpt; + u32 golpbkt; + u32 dsisetr; + int ret; ret = pm_runtime_resume_and_get(dsi->dev); if (ret < 0) return ret; - clk_set_rate(dsi->vclk, mode->clock * 1000); + ret = dsi->info->dphy_conf_clks(dsi, mode->clock, &hsfreq_millihz); + if (ret < 0) + goto err_phy; - ret = rzg2l_mipi_dsi_dphy_init(dsi, hsfreq); + ret = dsi->info->dphy_init(dsi, hsfreq_millihz); if (ret < 0) goto err_phy; @@ -307,6 +348,10 @@ static int rzg2l_mipi_dsi_startup(struct rzg2l_mipi_dsi *dsi, txsetr = TXSETR_DLEN | TXSETR_NUMLANEUSE(dsi->lanes - 1) | TXSETR_CLEN; rzg2l_mipi_dsi_link_write(dsi, TXSETR, txsetr); + if (dsi->info->dphy_startup_late_init) + dsi->info->dphy_startup_late_init(dsi); + + hsfreq = DIV_ROUND_CLOSEST_ULL(hsfreq_millihz, MILLI); /* * Global timings characteristic depends on high speed Clock Frequency * Currently MIPI DSI-IF just supports maximum FHD@60 with: @@ -315,12 +360,12 @@ static int rzg2l_mipi_dsi_startup(struct rzg2l_mipi_dsi *dsi, * - data lanes: maximum 4 lanes * Therefore maximum hsclk will be 891 Mbps. */ - if (hsfreq > 445500) { + if (hsfreq > 445500000) { clkkpt = 12; clkbfht = 15; clkstpt = 48; golpbkt = 75; - } else if (hsfreq > 250000) { + } else if (hsfreq > 250000000) { clkkpt = 7; clkbfht = 8; clkstpt = 27; @@ -351,7 +396,7 @@ static int rzg2l_mipi_dsi_startup(struct rzg2l_mipi_dsi *dsi, return 0; err_phy: - rzg2l_mipi_dsi_dphy_exit(dsi); + dsi->info->dphy_exit(dsi); pm_runtime_put(dsi->dev); return ret; @@ -359,7 +404,7 @@ err_phy: static void rzg2l_mipi_dsi_stop(struct rzg2l_mipi_dsi *dsi) { - rzg2l_mipi_dsi_dphy_exit(dsi); + dsi->info->dphy_exit(dsi); pm_runtime_put(dsi->dev); } @@ -609,9 +654,22 @@ rzg2l_mipi_dsi_bridge_mode_valid(struct drm_bridge *bridge, const struct drm_display_info *info, const struct drm_display_mode *mode) { - if (mode->clock > 148500) + struct rzg2l_mipi_dsi *dsi = bridge_to_rzg2l_mipi_dsi(bridge); + + if (mode->clock > dsi->info->max_dclk) return MODE_CLOCK_HIGH; + if (mode->clock < dsi->info->min_dclk) + return MODE_CLOCK_LOW; + + if (dsi->info->dphy_mode_clk_check) { + enum drm_mode_status status; + + status = dsi->info->dphy_mode_clk_check(dsi, mode->clock); + if (status != MODE_OK) + return status; + } + return MODE_OK; } @@ -645,8 +703,16 @@ static int rzg2l_mipi_dsi_host_attach(struct mipi_dsi_host *host, switch (mipi_dsi_pixel_format_to_bpp(device->format)) { case 24: + break; case 18: break; + case 16: + if (!(dsi->info->features & RZ_MIPI_DSI_FEATURE_16BPP)) { + dev_err(dsi->dev, "Unsupported format 0x%04x\n", + device->format); + return -EINVAL; + } + break; default: dev_err(dsi->dev, "Unsupported format 0x%04x\n", device->format); return -EINVAL; @@ -896,6 +962,8 @@ static int rzg2l_mipi_dsi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dsi); dsi->dev = &pdev->dev; + dsi->info = of_device_get_match_data(&pdev->dev); + ret = drm_of_get_data_lanes_count_ep(dsi->dev->of_node, 1, 0, 1, 4); if (ret < 0) return dev_err_probe(dsi->dev, ret, @@ -911,7 +979,7 @@ static int rzg2l_mipi_dsi_probe(struct platform_device *pdev) if (IS_ERR(dsi->vclk)) return PTR_ERR(dsi->vclk); - dsi->rstc = devm_reset_control_get_exclusive(dsi->dev, "rst"); + dsi->rstc = devm_reset_control_get_optional_exclusive(dsi->dev, "rst"); if (IS_ERR(dsi->rstc)) return dev_err_probe(dsi->dev, PTR_ERR(dsi->rstc), "failed to get rst\n"); @@ -939,13 +1007,13 @@ static int rzg2l_mipi_dsi_probe(struct platform_device *pdev) * mode->clock and format are not available. So initialize DPHY with * timing parameters for 80Mbps. */ - ret = rzg2l_mipi_dsi_dphy_init(dsi, 80000); + ret = dsi->info->dphy_init(dsi, 80000000ULL * MILLI); if (ret < 0) goto err_phy; txsetr = rzg2l_mipi_dsi_link_read(dsi, TXSETR); dsi->num_data_lanes = min(((txsetr >> 16) & 3) + 1, num_data_lanes); - rzg2l_mipi_dsi_dphy_exit(dsi); + dsi->info->dphy_exit(dsi); pm_runtime_put(dsi->dev); /* Initialize the DRM bridge. */ @@ -966,7 +1034,7 @@ static int rzg2l_mipi_dsi_probe(struct platform_device *pdev) return 0; err_phy: - rzg2l_mipi_dsi_dphy_exit(dsi); + dsi->info->dphy_exit(dsi); pm_runtime_put(dsi->dev); err_pm_disable: pm_runtime_disable(dsi->dev); @@ -983,8 +1051,17 @@ static void rzg2l_mipi_dsi_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } +static const struct rzg2l_mipi_dsi_hw_info rzg2l_mipi_dsi_info = { + .dphy_init = rzg2l_mipi_dsi_dphy_init, + .dphy_exit = rzg2l_mipi_dsi_dphy_exit, + .dphy_conf_clks = rzg2l_dphy_conf_clks, + .link_reg_offset = 0x10000, + .min_dclk = 5803, + .max_dclk = 148500, +}; + static const struct of_device_id rzg2l_mipi_dsi_of_table[] = { - { .compatible = "renesas,rzg2l-mipi-dsi" }, + { .compatible = "renesas,rzg2l-mipi-dsi", .data = &rzg2l_mipi_dsi_info, }, { /* sentinel */ } }; |
