As a final preparation step for adding support for the MediaTek Digital Video Out (DVO) hardware found in newer SoCs, create a new mtk_dpi_common library and move common code between the DPI and the upcoming DVO in there.
This change brings no functional differences, as the new file is built-in on the mediatek-drm object anyway. Signed-off-by: AngeloGioacchino Del Regno <[email protected]> --- drivers/gpu/drm/mediatek/Makefile | 1 + drivers/gpu/drm/mediatek/mtk_dpi.c | 654 +--------------------- drivers/gpu/drm/mediatek/mtk_dpi_common.c | 468 ++++++++++++++++ drivers/gpu/drm/mediatek/mtk_dpi_common.h | 276 +++++++++ 4 files changed, 762 insertions(+), 637 deletions(-) create mode 100644 drivers/gpu/drm/mediatek/mtk_dpi_common.c create mode 100644 drivers/gpu/drm/mediatek/mtk_dpi_common.h diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile index f40ad5565716..34a1d4721b70 100644 --- a/drivers/gpu/drm/mediatek/Makefile +++ b/drivers/gpu/drm/mediatek/Makefile @@ -15,6 +15,7 @@ mediatek-drm-y := mtk_crtc.o \ mtk_drm_drv.o \ mtk_drm_legacy.o \ mtk_dsi.o \ + mtk_dpi_common.o \ mtk_dpi.o \ mtk_ethdr.o \ mtk_mdp_rdma.o \ diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c index 077e8a42a044..ac972c0a9711 100644 --- a/drivers/gpu/drm/mediatek/mtk_dpi.c +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c @@ -2,17 +2,16 @@ /* * Copyright (c) 2014 MediaTek Inc. * Author: Jie Qiu <[email protected]> + * + * Copyright (c) 2026 Collabora Ltd. + * AngeloGioacchino Del Regno <[email protected]> */ #include <linux/bitfield.h> -#include <linux/clk.h> #include <linux/component.h> #include <linux/debugfs.h> -#include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/media-bus-format.h> -#include <linux/of.h> -#include <linux/of_graph.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <linux/soc/mediatek/mtk-mmsys.h> @@ -23,172 +22,12 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_bridge_connector.h> -#include <drm/drm_crtc.h> -#include <drm/drm_edid.h> #include <drm/drm_of.h> -#include <drm/drm_simple_kms_helper.h> #include "mtk_ddp_comp.h" #include "mtk_disp_drv.h" +#include "mtk_dpi_common.h" #include "mtk_dpi_regs.h" -#include "mtk_drm_drv.h" - -enum mtk_dpi_out_bit_num { - MTK_DPI_OUT_BIT_NUM_8BITS, - MTK_DPI_OUT_BIT_NUM_10BITS, - MTK_DPI_OUT_BIT_NUM_12BITS, - MTK_DPI_OUT_BIT_NUM_16BITS -}; - -enum mtk_dpi_out_yc_map { - MTK_DPI_OUT_YC_MAP_RGB, - MTK_DPI_OUT_YC_MAP_CYCY, - MTK_DPI_OUT_YC_MAP_YCYC, - MTK_DPI_OUT_YC_MAP_CY, - MTK_DPI_OUT_YC_MAP_YC -}; - -enum mtk_dpi_out_channel_swap { - MTK_DPI_OUT_CHANNEL_SWAP_RGB, - MTK_DPI_OUT_CHANNEL_SWAP_GBR, - MTK_DPI_OUT_CHANNEL_SWAP_BRG, - MTK_DPI_OUT_CHANNEL_SWAP_RBG, - MTK_DPI_OUT_CHANNEL_SWAP_GRB, - MTK_DPI_OUT_CHANNEL_SWAP_BGR -}; - -enum mtk_dpi_out_color_format { - MTK_DPI_COLOR_FORMAT_RGB, - MTK_DPI_COLOR_FORMAT_YCBCR_422, - MTK_DPI_COLOR_FORMAT_YCBCR_444 -}; - -struct mtk_dpi { - struct drm_encoder encoder; - struct drm_bridge bridge; - struct drm_bridge *next_bridge; - struct drm_connector *connector; - void __iomem *regs; - struct device *dev; - struct device *mmsys_dev; - struct clk *engine_clk; - struct clk *pixel_clk; - struct clk *tvd_clk; - int irq; - struct drm_display_mode mode; - const struct mtk_dpi_conf *conf; - enum mtk_dpi_out_color_format color_format; - enum mtk_dpi_out_yc_map yc_map; - enum mtk_dpi_out_bit_num bit_num; - enum mtk_dpi_out_channel_swap channel_swap; - struct pinctrl *pinctrl; - struct pinctrl_state *pins_gpio; - struct pinctrl_state *pins_dpi; - u32 output_fmt; - int refcount; -}; - -static inline struct mtk_dpi *bridge_to_dpi(struct drm_bridge *b) -{ - return container_of(b, struct mtk_dpi, bridge); -} - -enum mtk_dpi_polarity { - MTK_DPI_POLARITY_RISING, - MTK_DPI_POLARITY_FALLING, -}; - -struct mtk_dpi_polarities { - enum mtk_dpi_polarity de_pol; - enum mtk_dpi_polarity ck_pol; - enum mtk_dpi_polarity hsync_pol; - enum mtk_dpi_polarity vsync_pol; -}; - -struct mtk_dpi_sync_param { - u32 sync_width; - u32 front_porch; - u32 back_porch; - bool shift_half_line; -}; - -struct mtk_dpi_sync { - struct mtk_dpi_sync_param hsync; - struct mtk_dpi_sync_param vsync_l_odd; - struct mtk_dpi_sync_param vsync_l_even; - struct mtk_dpi_sync_param vsync_r_odd; - struct mtk_dpi_sync_param vsync_r_even; -}; - -struct mtk_dpi_yc_limit { - u16 y_top; - u16 y_bottom; - u16 c_top; - u16 c_bottom; -}; - -struct mtk_dpi_factor { - u32 clock; - u8 factor; -}; - -/** - * struct mtk_dpi_conf - Configuration of mediatek dpi. - * @dpi_factor: SoC-specific pixel clock PLL factor values. - * @num_dpi_factor: Number of pixel clock PLL factor values. - * @reg_h_fre_con: Register address of frequency control. - * @max_clock_khz: Max clock frequency supported for this SoCs in khz units. - * @edge_sel_en: Enable of edge selection. - * @output_fmts: Array of supported output formats. - * @num_output_fmts: Quantity of supported output formats. - * @is_ck_de_pol: Support CK/DE polarity. - * @swap_input_support: Support input swap function. - * @support_direct_pin: IP supports direct connection to dpi panels. - * @dimension_mask: Mask used for HWIDTH, HPORCH, VSYNC_WIDTH and VSYNC_PORCH - * (no shift). - * @hvsize_mask: Mask of HSIZE and VSIZE mask (no shift). - * @channel_swap_shift: Shift value of channel swap. - * @yuv422_en_bit: Enable bit of yuv422. - * @csc_enable_bit: Enable bit of CSC. - * @input_2p_en_bit: Enable bit for input two pixel per round feature. - * If present, implies that the feature must be enabled. - * @pixels_per_iter: Quantity of transferred pixels per iteration. - * @edge_cfg_in_mmsys: If the edge configuration for DPI's output needs to be set in MMSYS. - * @clocked_by_hdmi: HDMI IP outputs clock to dpi_pixel_clk input clock, needed - * for DPI registers access. - * @output_1pixel: Enable outputting one pixel per round; if the input is two pixel per - * round, the DPI hardware will internally transform it to 1T1P. - */ -struct mtk_dpi_conf { - const struct mtk_dpi_factor *dpi_factor; - const u8 num_dpi_factor; - u32 reg_h_fre_con; - u32 max_clock_khz; - bool edge_sel_en; - const u32 *output_fmts; - u32 num_output_fmts; - bool is_ck_de_pol; - bool swap_input_support; - bool support_direct_pin; - u32 dimension_mask; - u32 hvsize_mask; - u32 channel_swap_shift; - u32 yuv422_en_bit; - u32 csc_enable_bit; - u32 input_2p_en_bit; - u32 pixels_per_iter; - bool edge_cfg_in_mmsys; - bool clocked_by_hdmi; - bool output_1pixel; -}; - -static void mtk_dpi_mask(struct mtk_dpi *dpi, u32 offset, u32 val, u32 mask) -{ - u32 tmp = readl(dpi->regs + offset) & ~mask; - - tmp |= (val & mask); - writel(tmp, dpi->regs + offset); -} static void mtk_dpi_test_pattern_en(struct mtk_dpi *dpi, u8 type, bool enable) { @@ -466,165 +305,6 @@ static void mtk_dpi_dual_edge(struct mtk_dpi *dpi) } } -static void mtk_dpi_power_off(struct mtk_dpi *dpi) -{ - if (WARN_ON(dpi->refcount == 0)) - return; - - if (--dpi->refcount != 0) - return; - - clk_disable_unprepare(dpi->pixel_clk); - clk_disable_unprepare(dpi->tvd_clk); - clk_disable_unprepare(dpi->engine_clk); -} - -static int mtk_dpi_power_on(struct mtk_dpi *dpi) -{ - int ret; - - if (++dpi->refcount != 1) - return 0; - - ret = clk_prepare_enable(dpi->engine_clk); - if (ret) { - dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret); - goto err_refcount; - } - - ret = clk_prepare_enable(dpi->tvd_clk); - if (ret) { - dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret); - goto err_engine; - } - - ret = clk_prepare_enable(dpi->pixel_clk); - if (ret) { - dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret); - goto err_pixel; - } - - return 0; - -err_pixel: - clk_disable_unprepare(dpi->tvd_clk); -err_engine: - clk_disable_unprepare(dpi->engine_clk); -err_refcount: - dpi->refcount--; - return ret; -} - -static unsigned int mtk_dpi_calculate_factor(struct mtk_dpi *dpi, int mode_clk) -{ - const struct mtk_dpi_factor *dpi_factor = dpi->conf->dpi_factor; - int i; - - for (i = 0; i < dpi->conf->num_dpi_factor; i++) { - if (mode_clk <= dpi_factor[i].clock) - return dpi_factor[i].factor; - } - - /* If no match try the lowest possible factor */ - return dpi_factor[dpi->conf->num_dpi_factor - 1].factor; -} - -static void mtk_dpi_set_pixel_clk(struct mtk_dpi *dpi, struct videomode *vm, int mode_clk) -{ - unsigned long pll_rate; - unsigned int factor; - - /* let pll_rate can fix the valid range of tvdpll (1G~2GHz) */ - factor = mtk_dpi_calculate_factor(dpi, mode_clk); - pll_rate = vm->pixelclock * factor; - - dev_dbg(dpi->dev, "Want PLL %lu Hz, pixel clock %lu Hz\n", - pll_rate, vm->pixelclock); - - clk_set_rate(dpi->tvd_clk, pll_rate); - pll_rate = clk_get_rate(dpi->tvd_clk); - - /* - * Depending on the IP version, we may output a different amount of - * pixels for each iteration: divide the clock by this number and - * adjust the display porches accordingly. - */ - vm->pixelclock = pll_rate / factor; - vm->pixelclock /= dpi->conf->pixels_per_iter; - - if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) || - (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE)) - clk_set_rate(dpi->pixel_clk, vm->pixelclock * 2); - else - clk_set_rate(dpi->pixel_clk, vm->pixelclock); - - vm->pixelclock = clk_get_rate(dpi->pixel_clk); - - dev_dbg(dpi->dev, "Got PLL %lu Hz, pixel clock %lu Hz\n", - pll_rate, vm->pixelclock); -} - -static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi, struct videomode *vm, - struct mtk_dpi_sync *sync, - struct mtk_dpi_polarities *dpi_pol, - struct mtk_dpi_yc_limit *limit) -{ - struct drm_display_mode *mode = &dpi->mode; - - drm_display_mode_to_videomode(mode, vm); - - if (!dpi->conf->clocked_by_hdmi) - mtk_dpi_set_pixel_clk(dpi, vm, mode->clock); - - dpi_pol->ck_pol = MTK_DPI_POLARITY_FALLING; - dpi_pol->de_pol = MTK_DPI_POLARITY_RISING; - dpi_pol->hsync_pol = vm->flags & DISPLAY_FLAGS_HSYNC_HIGH ? - MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING; - dpi_pol->vsync_pol = vm->flags & DISPLAY_FLAGS_VSYNC_HIGH ? - MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING; - - /* - * Depending on the IP version, we may output a different amount of - * pixels for each iteration: adjust the display porches accordingly. - */ - sync->hsync.sync_width = vm->hsync_len / dpi->conf->pixels_per_iter; - sync->hsync.back_porch = vm->hback_porch / dpi->conf->pixels_per_iter; - sync->hsync.front_porch = vm->hfront_porch / dpi->conf->pixels_per_iter; - sync->hsync.shift_half_line = false; - - sync->vsync_l_odd.sync_width = vm->vsync_len; - sync->vsync_l_odd.back_porch = vm->vback_porch; - sync->vsync_l_odd.front_porch = vm->vfront_porch; - sync->vsync_l_odd.shift_half_line = false; - - if (vm->flags & DISPLAY_FLAGS_INTERLACED) { - sync->vsync_l_even = sync->vsync_l_odd; - sync->vsync_l_even.shift_half_line = true; - - if (mode->flags & DRM_MODE_FLAG_3D_MASK) { - sync->vsync_r_odd = sync->vsync_l_odd; - sync->vsync_r_even = sync->vsync_l_odd; - sync->vsync_r_even.shift_half_line = true; - } - } else if (mode->flags & DRM_MODE_FLAG_3D_MASK) { - sync->vsync_r_odd = sync->vsync_l_odd; - } - - if (drm_default_rgb_quant_range(&dpi->mode) == HDMI_QUANTIZATION_RANGE_LIMITED) { - limit->y_bottom = 0x10; - limit->y_top = 0xfe0; - limit->c_bottom = 0x10; - limit->c_top = 0xfe0; - } else { - limit->y_bottom = 0; - limit->y_top = 0xfff; - limit->c_bottom = 0; - limit->c_top = 0xfff; - } - - return 0; -} - static void mtk_dpi_config_hw(struct mtk_dpi *dpi, struct videomode *vm, struct mtk_dpi_sync *sync, struct mtk_dpi_polarities *dpi_pol, @@ -680,143 +360,6 @@ static void mtk_dpi_config_hw(struct mtk_dpi *dpi, return; } -static u32 *mtk_dpi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge, - struct drm_bridge_state *bridge_state, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state, - unsigned int *num_output_fmts) -{ - struct mtk_dpi *dpi = bridge_to_dpi(bridge); - u32 *output_fmts; - - *num_output_fmts = 0; - - if (!dpi->conf->output_fmts) { - dev_err(dpi->dev, "output_fmts should not be null\n"); - return NULL; - } - - output_fmts = kcalloc(dpi->conf->num_output_fmts, sizeof(*output_fmts), - GFP_KERNEL); - if (!output_fmts) - return NULL; - - *num_output_fmts = dpi->conf->num_output_fmts; - - memcpy(output_fmts, dpi->conf->output_fmts, - sizeof(*output_fmts) * dpi->conf->num_output_fmts); - - return output_fmts; -} - -static u32 *mtk_dpi_bridge_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) -{ - u32 *input_fmts; - - *num_input_fmts = 0; - - input_fmts = kcalloc(1, sizeof(*input_fmts), - GFP_KERNEL); - if (!input_fmts) - return NULL; - - *num_input_fmts = 1; - input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24; - - return input_fmts; -} - -static unsigned int mtk_dpi_bus_fmt_bit_num(unsigned int out_bus_format) -{ - switch (out_bus_format) { - default: - case MEDIA_BUS_FMT_RGB888_1X24: - case MEDIA_BUS_FMT_BGR888_1X24: - case MEDIA_BUS_FMT_RGB888_2X12_LE: - case MEDIA_BUS_FMT_RGB888_2X12_BE: - case MEDIA_BUS_FMT_YUYV8_1X16: - case MEDIA_BUS_FMT_YUV8_1X24: - return MTK_DPI_OUT_BIT_NUM_8BITS; - case MEDIA_BUS_FMT_RGB101010_1X30: - case MEDIA_BUS_FMT_YUYV10_1X20: - case MEDIA_BUS_FMT_YUV10_1X30: - return MTK_DPI_OUT_BIT_NUM_10BITS; - case MEDIA_BUS_FMT_YUYV12_1X24: - return MTK_DPI_OUT_BIT_NUM_12BITS; - } -} - -static unsigned int mtk_dpi_bus_fmt_channel_swap(unsigned int out_bus_format) -{ - switch (out_bus_format) { - default: - case MEDIA_BUS_FMT_RGB888_1X24: - case MEDIA_BUS_FMT_RGB888_2X12_LE: - case MEDIA_BUS_FMT_RGB888_2X12_BE: - case MEDIA_BUS_FMT_RGB101010_1X30: - case MEDIA_BUS_FMT_YUYV8_1X16: - case MEDIA_BUS_FMT_YUYV10_1X20: - case MEDIA_BUS_FMT_YUYV12_1X24: - return MTK_DPI_OUT_CHANNEL_SWAP_RGB; - case MEDIA_BUS_FMT_BGR888_1X24: - case MEDIA_BUS_FMT_YUV8_1X24: - case MEDIA_BUS_FMT_YUV10_1X30: - return MTK_DPI_OUT_CHANNEL_SWAP_BGR; - } -} - -static unsigned int mtk_dpi_bus_fmt_color_format(unsigned int out_bus_format) -{ - switch (out_bus_format) { - default: - case MEDIA_BUS_FMT_RGB888_1X24: - case MEDIA_BUS_FMT_BGR888_1X24: - case MEDIA_BUS_FMT_RGB888_2X12_LE: - case MEDIA_BUS_FMT_RGB888_2X12_BE: - case MEDIA_BUS_FMT_RGB101010_1X30: - return MTK_DPI_COLOR_FORMAT_RGB; - case MEDIA_BUS_FMT_YUYV8_1X16: - case MEDIA_BUS_FMT_YUYV10_1X20: - case MEDIA_BUS_FMT_YUYV12_1X24: - return MTK_DPI_COLOR_FORMAT_YCBCR_422; - case MEDIA_BUS_FMT_YUV8_1X24: - case MEDIA_BUS_FMT_YUV10_1X30: - return MTK_DPI_COLOR_FORMAT_YCBCR_444; - } -} - -static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge, - struct drm_bridge_state *bridge_state, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) -{ - struct mtk_dpi *dpi = bridge_to_dpi(bridge); - unsigned int out_bus_format; - - out_bus_format = bridge_state->output_bus_cfg.format; - - if (out_bus_format == MEDIA_BUS_FMT_FIXED) - if (dpi->conf->num_output_fmts) - out_bus_format = dpi->conf->output_fmts[0]; - - dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n", - bridge_state->input_bus_cfg.format, - bridge_state->output_bus_cfg.format); - - dpi->output_fmt = out_bus_format; - dpi->bit_num = mtk_dpi_bus_fmt_bit_num(out_bus_format); - dpi->channel_swap = mtk_dpi_bus_fmt_channel_swap(out_bus_format); - dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB; - dpi->color_format = mtk_dpi_bus_fmt_color_format(out_bus_format); - - return 0; -} - static int mtk_dpi_bridge_attach(struct drm_bridge *bridge, struct drm_encoder *encoder, enum drm_bridge_attach_flags flags) @@ -827,15 +370,6 @@ static int mtk_dpi_bridge_attach(struct drm_bridge *bridge, &dpi->bridge, flags); } -static void mtk_dpi_bridge_mode_set(struct drm_bridge *bridge, - const struct drm_display_mode *mode, - const struct drm_display_mode *adjusted_mode) -{ - struct mtk_dpi *dpi = bridge_to_dpi(bridge); - - drm_mode_copy(&dpi->mode, adjusted_mode); -} - static void mtk_dpi_bridge_disable(struct drm_bridge *bridge) { struct mtk_dpi *dpi = bridge_to_dpi(bridge); @@ -895,39 +429,7 @@ static int mtk_dpi_debug_tp_show(struct seq_file *m, void *arg) en = val & DPI_PAT_EN; val = FIELD_GET(DPI_PAT_SEL, val); - seq_printf(m, "DPI Test Pattern: %s\n", en ? "Enabled" : "Disabled"); - - if (en) { - seq_printf(m, "Internal pattern %d: ", val); - switch (val) { - case 0: - seq_puts(m, "256 Vertical Gray\n"); - break; - case 1: - seq_puts(m, "1024 Vertical Gray\n"); - break; - case 2: - seq_puts(m, "256 Horizontal Gray\n"); - break; - case 3: - seq_puts(m, "1024 Horizontal Gray\n"); - break; - case 4: - seq_puts(m, "Vertical Color bars\n"); - break; - case 6: - seq_puts(m, "Frame border\n"); - break; - case 7: - seq_puts(m, "Dot moire\n"); - break; - default: - seq_puts(m, "Invalid selection\n"); - break; - } - } - - return 0; + return mtk_dpi_common_debug_tp_show(m, en, val); } static ssize_t mtk_dpi_debug_tp_write(struct file *file, const char __user *ubuf, @@ -935,23 +437,15 @@ static ssize_t mtk_dpi_debug_tp_write(struct file *file, const char __user *ubuf { struct seq_file *m = file->private_data; u32 en, type; - char buf[6]; - - if (!m || !m->private || *offp || len > sizeof(buf) - 1) - return -EINVAL; - - memset(buf, 0, sizeof(buf)); - if (copy_from_user(buf, ubuf, len)) - return -EFAULT; + ssize_t ret; - if (sscanf(buf, "%u %u", &en, &type) != 2) - return -EINVAL; - - if (en < 0 || en > 1 || type < 0 || type > 7) - return -EINVAL; + /* seq_file and dpi pointers are checked by mtk_dpi_common_debug_tp_write() */ + ret = mtk_dpi_common_debug_tp_write(file, ubuf, len, offp, &en, &type); + if (ret < 0) + return ret; mtk_dpi_test_pattern_en((struct mtk_dpi *)m->private, type, en); - return len; + return ret; } static int mtk_dpi_debug_tp_open(struct inode *inode, struct file *file) @@ -1020,56 +514,9 @@ unsigned int mtk_dpi_encoder_index(struct device *dev) return encoder_index; } -static int mtk_dpi_bind(struct device *dev, struct device *master, void *data) -{ - struct mtk_dpi *dpi = dev_get_drvdata(dev); - struct drm_device *drm_dev = data; - struct mtk_drm_private *priv = drm_dev->dev_private; - int ret; - - dpi->mmsys_dev = priv->mmsys_dev; - ret = drm_simple_encoder_init(drm_dev, &dpi->encoder, - DRM_MODE_ENCODER_TMDS); - if (ret) { - dev_err(dev, "Failed to initialize decoder: %d\n", ret); - return ret; - } - - ret = mtk_find_possible_crtcs(drm_dev, dpi->dev); - if (ret < 0) - goto err_cleanup; - dpi->encoder.possible_crtcs = ret; - - ret = drm_bridge_attach(&dpi->encoder, &dpi->bridge, NULL, - DRM_BRIDGE_ATTACH_NO_CONNECTOR); - if (ret) - goto err_cleanup; - - dpi->connector = drm_bridge_connector_init(drm_dev, &dpi->encoder); - if (IS_ERR(dpi->connector)) { - dev_err(dev, "Unable to create bridge connector\n"); - ret = PTR_ERR(dpi->connector); - goto err_cleanup; - } - - return 0; - -err_cleanup: - drm_encoder_cleanup(&dpi->encoder); - return ret; -} - -static void mtk_dpi_unbind(struct device *dev, struct device *master, - void *data) -{ - struct mtk_dpi *dpi = dev_get_drvdata(dev); - - drm_encoder_cleanup(&dpi->encoder); -} - static const struct component_ops mtk_dpi_component_ops = { - .bind = mtk_dpi_bind, - .unbind = mtk_dpi_unbind, + .bind = mtk_dpi_common_bind, + .unbind = mtk_dpi_common_unbind, }; static const u32 mt8173_output_fmts[] = { @@ -1249,84 +696,16 @@ static const struct mtk_dpi_conf mt8195_dpintf_conf = { static int mtk_dpi_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; struct mtk_dpi *dpi; int ret; - dpi = devm_drm_bridge_alloc(dev, struct mtk_dpi, bridge, - &mtk_dpi_bridge_funcs); + dpi = mtk_dpi_common_probe(pdev, &mtk_dpi_bridge_funcs); if (IS_ERR(dpi)) return PTR_ERR(dpi); - dpi->dev = dev; - dpi->conf = (struct mtk_dpi_conf *)of_device_get_match_data(dev); - dpi->output_fmt = MEDIA_BUS_FMT_RGB888_1X24; - - dpi->pinctrl = devm_pinctrl_get(&pdev->dev); - if (IS_ERR(dpi->pinctrl)) { - dpi->pinctrl = NULL; - dev_dbg(&pdev->dev, "Cannot find pinctrl!\n"); - } - if (dpi->pinctrl) { - dpi->pins_gpio = pinctrl_lookup_state(dpi->pinctrl, "sleep"); - if (IS_ERR(dpi->pins_gpio)) { - dpi->pins_gpio = NULL; - dev_dbg(&pdev->dev, "Cannot find pinctrl idle!\n"); - } - if (dpi->pins_gpio) - pinctrl_select_state(dpi->pinctrl, dpi->pins_gpio); - - dpi->pins_dpi = pinctrl_lookup_state(dpi->pinctrl, "default"); - if (IS_ERR(dpi->pins_dpi)) { - dpi->pins_dpi = NULL; - dev_dbg(&pdev->dev, "Cannot find pinctrl active!\n"); - } - } - dpi->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(dpi->regs)) - return dev_err_probe(dev, PTR_ERR(dpi->regs), - "Failed to ioremap mem resource\n"); - - dpi->engine_clk = devm_clk_get(dev, "engine"); - if (IS_ERR(dpi->engine_clk)) - return dev_err_probe(dev, PTR_ERR(dpi->engine_clk), - "Failed to get engine clock\n"); - - dpi->pixel_clk = devm_clk_get(dev, "pixel"); - if (IS_ERR(dpi->pixel_clk)) - return dev_err_probe(dev, PTR_ERR(dpi->pixel_clk), - "Failed to get pixel clock\n"); - - dpi->tvd_clk = devm_clk_get(dev, "pll"); - if (IS_ERR(dpi->tvd_clk)) - return dev_err_probe(dev, PTR_ERR(dpi->tvd_clk), - "Failed to get tvdpll clock\n"); - - dpi->irq = platform_get_irq(pdev, 0); - if (dpi->irq < 0) - return dpi->irq; - - dpi->next_bridge = devm_drm_of_get_bridge(dpi->dev, dpi->dev->of_node, 1, -1); - if (IS_ERR(dpi->next_bridge) && PTR_ERR(dpi->next_bridge) == -ENODEV) { - /* Old devicetree has only one endpoint */ - dpi->next_bridge = devm_drm_of_get_bridge(dpi->dev, dpi->dev->of_node, 0, 0); - } - if (IS_ERR(dpi->next_bridge)) - return dev_err_probe(dpi->dev, PTR_ERR(dpi->next_bridge), - "Failed to get bridge\n"); - - platform_set_drvdata(pdev, dpi); - - dpi->bridge.of_node = dev->of_node; - dpi->bridge.type = DRM_MODE_CONNECTOR_DPI; - - ret = devm_drm_bridge_add(dev, &dpi->bridge); - if (ret) - return ret; - - ret = component_add(dev, &mtk_dpi_component_ops); + ret = component_add(&pdev->dev, &mtk_dpi_component_ops); if (ret) - return dev_err_probe(dev, ret, "Failed to add component.\n"); + return dev_err_probe(&pdev->dev, ret, "Failed to add component.\n"); return 0; } @@ -1357,3 +736,4 @@ struct platform_driver mtk_dpi_driver = { .of_match_table = mtk_dpi_of_ids, }, }; +MODULE_IMPORT_NS("DRM_MTK_DPI"); diff --git a/drivers/gpu/drm/mediatek/mtk_dpi_common.c b/drivers/gpu/drm/mediatek/mtk_dpi_common.c new file mode 100644 index 000000000000..b9e7b053d278 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dpi_common.c @@ -0,0 +1,468 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 MediaTek Inc. + * Copyright (c) 2026 Collabora Ltd. + * AngeloGioacchino Del Regno <[email protected]> + */ + +#include <linux/clk.h> +#include <linux/debugfs.h> +#include <linux/kernel.h> +#include <linux/media-bus-format.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> +#include <drm/drm_bridge.h> +#include <drm/drm_bridge_connector.h> +#include <drm/drm_connector.h> +#include <drm/drm_edid.h> +#include <drm/drm_encoder.h> +#include <drm/drm_modes.h> +#include <drm/drm_simple_kms_helper.h> + +#include "mtk_ddp_comp.h" +#include "mtk_dpi_common.h" +#include "mtk_drm_drv.h" + +void mtk_dpi_power_off(struct mtk_dpi *dpi) +{ + if (WARN_ON(dpi->refcount == 0)) + return; + + if (--dpi->refcount != 0) + return; + + clk_disable_unprepare(dpi->pixel_clk); + clk_disable_unprepare(dpi->tvd_clk); + clk_disable_unprepare(dpi->engine_clk); +} +EXPORT_SYMBOL_NS_GPL(mtk_dpi_power_off, "DRM_MTK_DPI"); + +int mtk_dpi_power_on(struct mtk_dpi *dpi) +{ + int ret; + + if (++dpi->refcount != 1) + return 0; + + ret = clk_prepare_enable(dpi->engine_clk); + if (ret) { + dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret); + goto err_refcount; + } + + ret = clk_prepare_enable(dpi->tvd_clk); + if (ret) { + dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret); + goto err_engine; + } + + ret = clk_prepare_enable(dpi->pixel_clk); + if (ret) { + dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret); + goto err_pixel; + } + + return 0; + +err_pixel: + clk_disable_unprepare(dpi->tvd_clk); +err_engine: + clk_disable_unprepare(dpi->engine_clk); +err_refcount: + dpi->refcount--; + return ret; +} +EXPORT_SYMBOL_NS_GPL(mtk_dpi_power_on, "DRM_MTK_DPI"); + +static unsigned int mtk_dpi_calculate_factor(struct mtk_dpi *dpi, int mode_clk) +{ + const struct mtk_dpi_factor *dpi_factor = dpi->conf->dpi_factor; + int i; + + for (i = 0; i < dpi->conf->num_dpi_factor; i++) { + if (mode_clk <= dpi_factor[i].clock) + return dpi_factor[i].factor; + } + + /* If no match try the lowest possible factor */ + return dpi_factor[dpi->conf->num_dpi_factor - 1].factor; +} + +static void mtk_dpi_set_pixel_clk(struct mtk_dpi *dpi, struct videomode *vm, int mode_clk) +{ + unsigned long pll_rate; + unsigned int factor; + + /* let pll_rate can fix the valid range of tvdpll (1G~2GHz) */ + factor = mtk_dpi_calculate_factor(dpi, mode_clk); + pll_rate = vm->pixelclock * factor; + + dev_dbg(dpi->dev, "Want PLL %lu Hz, pixel clock %lu Hz\n", + pll_rate, vm->pixelclock); + + clk_set_rate(dpi->tvd_clk, pll_rate); + pll_rate = clk_get_rate(dpi->tvd_clk); + + /* + * Depending on the IP version, we may output a different amount of + * pixels for each iteration: divide the clock by this number. + */ + vm->pixelclock = pll_rate / factor; + vm->pixelclock /= dpi->conf->pixels_per_iter; + + if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) || + (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE)) + clk_set_rate(dpi->pixel_clk, vm->pixelclock * 2); + else + clk_set_rate(dpi->pixel_clk, vm->pixelclock); + + vm->pixelclock = clk_get_rate(dpi->pixel_clk); + + dev_dbg(dpi->dev, "Got PLL %lu Hz, pixel clock %lu Hz\n", + pll_rate, vm->pixelclock); +} + +int mtk_dpi_set_display_mode(struct mtk_dpi *dpi, struct videomode *vm, + struct mtk_dpi_sync *sync, + struct mtk_dpi_polarities *dpi_pol, + struct mtk_dpi_yc_limit *limit) +{ + struct drm_display_mode *mode = &dpi->mode; + + drm_display_mode_to_videomode(mode, vm); + + if (!dpi->conf->clocked_by_hdmi) + mtk_dpi_set_pixel_clk(dpi, vm, mode->clock); + + dpi_pol->ck_pol = MTK_DPI_POLARITY_FALLING; + dpi_pol->de_pol = MTK_DPI_POLARITY_RISING; + dpi_pol->hsync_pol = vm->flags & DISPLAY_FLAGS_HSYNC_HIGH ? + MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING; + dpi_pol->vsync_pol = vm->flags & DISPLAY_FLAGS_VSYNC_HIGH ? + MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING; + + /* + * Depending on the IP version, we may output a different amount of + * pixels for each iteration: adjust the display porches accordingly. + */ + sync->hsync.sync_width = vm->hsync_len / dpi->conf->pixels_per_iter; + sync->hsync.back_porch = vm->hback_porch / dpi->conf->pixels_per_iter; + sync->hsync.front_porch = vm->hfront_porch / dpi->conf->pixels_per_iter; + sync->hsync.shift_half_line = false; + + sync->vsync_l_odd.sync_width = vm->vsync_len; + sync->vsync_l_odd.back_porch = vm->vback_porch; + sync->vsync_l_odd.front_porch = vm->vfront_porch; + sync->vsync_l_odd.shift_half_line = false; + + if (vm->flags & DISPLAY_FLAGS_INTERLACED) { + sync->vsync_l_even = sync->vsync_l_odd; + sync->vsync_l_even.shift_half_line = true; + + if (mode->flags & DRM_MODE_FLAG_3D_MASK) { + sync->vsync_r_odd = sync->vsync_l_odd; + sync->vsync_r_even = sync->vsync_l_odd; + sync->vsync_r_even.shift_half_line = true; + } + } else if (mode->flags & DRM_MODE_FLAG_3D_MASK) { + sync->vsync_r_odd = sync->vsync_l_odd; + } + + if (drm_default_rgb_quant_range(&dpi->mode) == HDMI_QUANTIZATION_RANGE_LIMITED) { + limit->y_bottom = 0x10; + limit->y_top = 0xfe0; + limit->c_bottom = 0x10; + limit->c_top = 0xfe0; + } else { + limit->y_bottom = 0; + limit->y_top = 0xfff; + limit->c_bottom = 0; + limit->c_top = 0xfff; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(mtk_dpi_set_display_mode, "DRM_MTK_DPI"); + +u32 *mtk_dpi_bridge_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) +{ + u32 *input_fmts; + + *num_input_fmts = 0; + + input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL); + if (!input_fmts) + return NULL; + + *num_input_fmts = 1; + input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24; + + return input_fmts; +} +EXPORT_SYMBOL_NS_GPL(mtk_dpi_bridge_atomic_get_input_bus_fmts, "DRM_MTK_DPI"); + +u32 *mtk_dpi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + unsigned int *num_output_fmts) +{ + struct mtk_dpi *dpi = bridge_to_dpi(bridge); + u32 *output_fmts; + + *num_output_fmts = 0; + + if (!dpi->conf->output_fmts) { + dev_err(dpi->dev, "output_fmts should not be null\n"); + return NULL; + } + + output_fmts = kcalloc(dpi->conf->num_output_fmts, sizeof(*output_fmts), GFP_KERNEL); + if (!output_fmts) + return NULL; + + *num_output_fmts = dpi->conf->num_output_fmts; + + memcpy(output_fmts, dpi->conf->output_fmts, + sizeof(*output_fmts) * dpi->conf->num_output_fmts); + + return output_fmts; +} +EXPORT_SYMBOL_NS_GPL(mtk_dpi_bridge_atomic_get_output_bus_fmts, "DRM_MTK_DPI"); + +int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + unsigned int out_bus_format = bridge_state->output_bus_cfg.format; + struct mtk_dpi *dpi = bridge_to_dpi(bridge); + + if (out_bus_format == MEDIA_BUS_FMT_FIXED && dpi->conf->num_output_fmts) + out_bus_format = dpi->conf->output_fmts[0]; + + dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n", + bridge_state->input_bus_cfg.format, + bridge_state->output_bus_cfg.format); + + dpi->output_fmt = out_bus_format; + dpi->bit_num = mtk_dpi_bus_fmt_bit_num(out_bus_format); + dpi->channel_swap = mtk_dpi_bus_fmt_channel_swap(out_bus_format); + dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB; + dpi->color_format = mtk_dpi_bus_fmt_color_format(out_bus_format); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(mtk_dpi_bridge_atomic_check, "DRM_MTK_DPI"); + +void mtk_dpi_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + struct mtk_dpi *dpi = bridge_to_dpi(bridge); + + drm_mode_copy(&dpi->mode, adjusted_mode); +} +EXPORT_SYMBOL_NS_GPL(mtk_dpi_bridge_mode_set, "DRM_MTK_DPI"); + +int mtk_dpi_common_bind(struct device *dev, struct device *master, void *data) +{ + struct mtk_dpi *dpi = dev_get_drvdata(dev); + struct drm_device *drm_dev = data; + struct mtk_drm_private *priv = drm_dev->dev_private; + int ret; + + dpi->mmsys_dev = priv->mmsys_dev; + ret = drm_simple_encoder_init(drm_dev, &dpi->encoder, + DRM_MODE_ENCODER_TMDS); + if (ret) { + dev_err(dev, "Failed to initialize decoder: %d\n", ret); + return ret; + } + + ret = mtk_find_possible_crtcs(drm_dev, dpi->dev); + if (ret < 0) + goto err_cleanup; + dpi->encoder.possible_crtcs = ret; + + ret = drm_bridge_attach(&dpi->encoder, &dpi->bridge, NULL, + DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret) + goto err_cleanup; + + dpi->connector = drm_bridge_connector_init(drm_dev, &dpi->encoder); + if (IS_ERR(dpi->connector)) { + dev_err(dev, "Unable to create bridge connector\n"); + ret = PTR_ERR(dpi->connector); + goto err_cleanup; + } + + return 0; + +err_cleanup: + drm_encoder_cleanup(&dpi->encoder); + return ret; +} +EXPORT_SYMBOL_NS_GPL(mtk_dpi_common_bind, "DRM_MTK_DPI"); + +void mtk_dpi_common_unbind(struct device *dev, struct device *master, void *data) +{ + struct mtk_dpi *dpi = dev_get_drvdata(dev); + + drm_encoder_cleanup(&dpi->encoder); +} +EXPORT_SYMBOL_NS_GPL(mtk_dpi_common_unbind, "DRM_MTK_DPI"); + +int mtk_dpi_common_debug_tp_show(struct seq_file *m, bool en, u32 sel) +{ + seq_printf(m, "DPI Test Pattern: %s\n", en ? "Enabled" : "Disabled"); + + if (en) { + seq_printf(m, "Internal pattern %d: ", sel); + + switch (sel) { + case 0: + seq_puts(m, "256 Vertical Gray\n"); + break; + case 1: + seq_puts(m, "1024 Vertical Gray\n"); + break; + case 2: + seq_puts(m, "256 Horizontal Gray\n"); + break; + case 3: + seq_puts(m, "1024 Horizontal Gray\n"); + break; + case 4: + seq_puts(m, "Vertical Color bars\n"); + break; + case 6: + seq_puts(m, "Frame border\n"); + break; + case 7: + seq_puts(m, "Dot moire\n"); + break; + default: + seq_puts(m, "Invalid selection\n"); + break; + } + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(mtk_dpi_common_debug_tp_show, "DRM_MTK_DPI"); + +ssize_t mtk_dpi_common_debug_tp_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp, u32 *en, u32 *type) +{ + struct seq_file *m = file->private_data; + char buf[6]; + + if (!m || !m->private || *offp || len > sizeof(buf) - 1) + return -EINVAL; + + memset(buf, 0, sizeof(buf)); + if (copy_from_user(buf, ubuf, len)) + return -EFAULT; + + if (sscanf(buf, "%u %u", en, type) != 2) + return -EINVAL; + + if (*en < 0 || *en > 1 || *type < 0 || *type > 7) + return -EINVAL; + + return len; +} +EXPORT_SYMBOL_NS_GPL(mtk_dpi_common_debug_tp_write, "DRM_MTK_DPI"); + +struct mtk_dpi *mtk_dpi_common_probe(struct platform_device *pdev, + const struct drm_bridge_funcs *bridge_funcs) +{ + struct device *dev = &pdev->dev; + struct mtk_dpi *dpi; + int ret; + + dpi = devm_drm_bridge_alloc(dev, struct mtk_dpi, bridge, bridge_funcs); + if (IS_ERR(dpi)) + return dpi; + + dpi->dev = dev; + dpi->conf = (struct mtk_dpi_conf *)of_device_get_match_data(dev); + dpi->output_fmt = MEDIA_BUS_FMT_RGB888_1X24; + + dpi->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(dpi->pinctrl)) { + dev_dbg(dev, "Cannot find pinctrl!\n"); + dpi->pinctrl = NULL; + } else { + dpi->pins_gpio = pinctrl_lookup_state(dpi->pinctrl, "sleep"); + if (IS_ERR(dpi->pins_gpio)) { + dpi->pins_gpio = NULL; + dev_dbg(dev, "Cannot find pinctrl idle!\n"); + } + if (dpi->pins_gpio) + pinctrl_select_state(dpi->pinctrl, dpi->pins_gpio); + + dpi->pins_dpi = pinctrl_lookup_state(dpi->pinctrl, "default"); + if (IS_ERR(dpi->pins_dpi)) { + dpi->pins_dpi = NULL; + dev_dbg(dev, "Cannot find pinctrl active!\n"); + } + } + + dpi->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dpi->regs)) + return dev_err_ptr_probe(dev, PTR_ERR(dpi->regs), + "Failed to ioremap mem resource\n"); + + dpi->engine_clk = devm_clk_get(dev, "engine"); + if (IS_ERR(dpi->engine_clk)) + return dev_err_ptr_probe(dev, PTR_ERR(dpi->engine_clk), + "Failed to get engine clock\n"); + + dpi->pixel_clk = devm_clk_get(dev, "pixel"); + if (IS_ERR(dpi->pixel_clk)) + return dev_err_ptr_probe(dev, PTR_ERR(dpi->pixel_clk), + "Failed to get pixel clock\n"); + + dpi->tvd_clk = devm_clk_get(dev, "pll"); + if (IS_ERR(dpi->tvd_clk)) + return dev_err_ptr_probe(dev, PTR_ERR(dpi->tvd_clk), + "Failed to get tvdpll clock\n"); + + dpi->irq = platform_get_irq(pdev, 0); + if (dpi->irq < 0) + return ERR_PTR(dpi->irq); + + dpi->next_bridge = devm_drm_of_get_bridge(dpi->dev, dpi->dev->of_node, 1, -1); + if (IS_ERR(dpi->next_bridge) && PTR_ERR(dpi->next_bridge) == -ENODEV) { + /* Old devicetree has only one endpoint */ + dpi->next_bridge = devm_drm_of_get_bridge(dpi->dev, dpi->dev->of_node, 0, 0); + } + if (IS_ERR(dpi->next_bridge)) + return dev_err_ptr_probe(dpi->dev, PTR_ERR(dpi->next_bridge), + "Failed to get bridge\n"); + + platform_set_drvdata(pdev, dpi); + + dpi->bridge.of_node = dev->of_node; + dpi->bridge.type = DRM_MODE_CONNECTOR_DPI; + + ret = devm_drm_bridge_add(dev, &dpi->bridge); + if (ret) + return ERR_PTR(ret); + + return dpi; +} +EXPORT_SYMBOL_NS_GPL(mtk_dpi_common_probe, "DRM_MTK_DPI"); + +MODULE_AUTHOR("AngeloGioacchino Del Regno <[email protected]>"); +MODULE_DESCRIPTION("MediaTek DPI/DVO Common Library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/mediatek/mtk_dpi_common.h b/drivers/gpu/drm/mediatek/mtk_dpi_common.h new file mode 100644 index 000000000000..c5cc26c7900d --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dpi_common.h @@ -0,0 +1,276 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. + * Copyright (c) 2026 Collabora Ltd. + * AngeloGioacchino Del Regno <[email protected]> + */ + +#ifndef _MTK_DPI_COMMON_H +#define _MTK_DPI_COMMON_H + +#include <linux/clk.h> +#include <linux/pinctrl/consumer.h> +#include <uapi/linux/media-bus-format.h> +#include <drm/drm_bridge.h> +#include <drm/drm_connector.h> +#include <drm/drm_encoder.h> +#include <drm/drm_modes.h> +#include <video/videomode.h> + +enum mtk_dpi_out_bit_num { + MTK_DPI_OUT_BIT_NUM_8BITS, + MTK_DPI_OUT_BIT_NUM_10BITS, + MTK_DPI_OUT_BIT_NUM_12BITS, + MTK_DPI_OUT_BIT_NUM_16BITS +}; + +enum mtk_dpi_out_yc_map { + MTK_DPI_OUT_YC_MAP_RGB, + MTK_DPI_OUT_YC_MAP_CYCY, + MTK_DPI_OUT_YC_MAP_YCYC, + MTK_DPI_OUT_YC_MAP_CY, + MTK_DPI_OUT_YC_MAP_YC +}; + +enum mtk_dpi_out_channel_swap { + MTK_DPI_OUT_CHANNEL_SWAP_RGB, + MTK_DPI_OUT_CHANNEL_SWAP_GBR, + MTK_DPI_OUT_CHANNEL_SWAP_BRG, + MTK_DPI_OUT_CHANNEL_SWAP_RBG, + MTK_DPI_OUT_CHANNEL_SWAP_GRB, + MTK_DPI_OUT_CHANNEL_SWAP_BGR +}; + +enum mtk_dpi_out_color_format { + MTK_DPI_COLOR_FORMAT_RGB, + MTK_DPI_COLOR_FORMAT_YCBCR_422, + MTK_DPI_COLOR_FORMAT_YCBCR_444 +}; + +struct mtk_dpi { + struct drm_encoder encoder; + struct drm_bridge bridge; + struct drm_bridge *next_bridge; + struct drm_connector *connector; + void __iomem *regs; + struct device *dev; + struct device *mmsys_dev; + struct clk *engine_clk; + struct clk *pixel_clk; + struct clk *tvd_clk; + int irq; + struct drm_display_mode mode; + const struct mtk_dpi_conf *conf; + enum mtk_dpi_out_color_format color_format; + enum mtk_dpi_out_yc_map yc_map; + enum mtk_dpi_out_bit_num bit_num; + enum mtk_dpi_out_channel_swap channel_swap; + struct pinctrl *pinctrl; + struct pinctrl_state *pins_gpio; + struct pinctrl_state *pins_dpi; + u32 output_fmt; + s8 refcount; +}; + +enum mtk_dpi_polarity { + MTK_DPI_POLARITY_RISING, + MTK_DPI_POLARITY_FALLING, +}; + +struct mtk_dpi_polarities { + enum mtk_dpi_polarity de_pol; + enum mtk_dpi_polarity ck_pol; + enum mtk_dpi_polarity hsync_pol; + enum mtk_dpi_polarity vsync_pol; +}; + +struct mtk_dpi_sync_param { + u32 sync_width; + u32 front_porch; + u32 back_porch; + bool shift_half_line; +}; + +struct mtk_dpi_sync { + struct mtk_dpi_sync_param hsync; + struct mtk_dpi_sync_param vsync_l_odd; + struct mtk_dpi_sync_param vsync_l_even; + struct mtk_dpi_sync_param vsync_r_odd; + struct mtk_dpi_sync_param vsync_r_even; +}; + +struct mtk_dpi_yc_limit { + u16 y_top; + u16 y_bottom; + u16 c_top; + u16 c_bottom; +}; + +struct mtk_dpi_factor { + u32 clock; + u8 factor; +}; + +/** + * struct mtk_dpi_conf - Configuration of MediaTek DPI/DVO + * @dpi_factor: SoC-specific pixel clock PLL factor values. + * @num_dpi_factor: Number of pixel clock PLL factor values. + * @reg_h_fre_con: Register address of frequency control. + * @max_clock_khz: Max clock frequency supported for this SoCs in khz units. + * @edge_sel_en: Enable of edge selection. + * @output_fmts: Array of supported output formats. + * @num_output_fmts: Quantity of supported output formats. + * @is_ck_de_pol: Support CK/DE polarity. + * @swap_input_support: Support input swap function. + * @support_direct_pin: IP supports direct connection to dpi panels. + * @dimension_mask: Mask used for HWIDTH, HPORCH, VSYNC_WIDTH and VSYNC_PORCH + * (no shift). + * @hvsize_mask: Mask of HSIZE and VSIZE mask (no shift). + * @channel_swap_shift: Shift value of channel swap. + * @yuv422_en_bit: Enable bit of yuv422. + * @csc_enable_bit: Enable bit of CSC. + * @input_2p_en_bit: Enable bit for input two pixel per round feature. + * If present, implies that the feature must be enabled. + * @pixels_per_iter: Quantity of transferred pixels per iteration. + * @edge_cfg_in_mmsys: If the edge configuration for DPI's output needs to be set in MMSYS. + * @clocked_by_hdmi: HDMI IP outputs clock to dpi_pixel_clk input clock, needed + * for DPI registers access. + * @output_1pixel: Enable outputting one pixel per round; if the input is two pixel per + * round, the DPI hardware will internally transform it to 1T1P. + */ +struct mtk_dpi_conf { + const struct mtk_dpi_factor *dpi_factor; + const u8 num_dpi_factor; + u32 reg_h_fre_con; + u32 max_clock_khz; + bool edge_sel_en; + const u32 *output_fmts; + u32 num_output_fmts; + bool is_ck_de_pol; + bool swap_input_support; + bool support_direct_pin; + u32 dimension_mask; + u32 hvsize_mask; + u32 channel_swap_shift; + u32 yuv422_en_bit; + u32 csc_enable_bit; + u32 input_2p_en_bit; + u32 pixels_per_iter; + bool edge_cfg_in_mmsys; + bool clocked_by_hdmi; + bool output_1pixel; +}; + +static inline struct mtk_dpi *bridge_to_dpi(struct drm_bridge *b) +{ + return container_of(b, struct mtk_dpi, bridge); +} + +static inline void mtk_dpi_mask(struct mtk_dpi *dpi, u32 offset, u32 val, u32 mask) +{ + u32 tmp = readl(dpi->regs + offset); + + tmp &= ~mask; + tmp |= (val & mask); + writel(tmp, dpi->regs + offset); +} + +static inline enum mtk_dpi_out_bit_num +mtk_dpi_bus_fmt_bit_num(unsigned int out_bus_format) +{ + switch (out_bus_format) { + default: + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_BGR888_1X24: + case MEDIA_BUS_FMT_RGB888_2X12_LE: + case MEDIA_BUS_FMT_RGB888_2X12_BE: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YUV8_1X24: + return MTK_DPI_OUT_BIT_NUM_8BITS; + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_YUYV10_1X20: + case MEDIA_BUS_FMT_YUV10_1X30: + return MTK_DPI_OUT_BIT_NUM_10BITS; + case MEDIA_BUS_FMT_YUYV12_1X24: + return MTK_DPI_OUT_BIT_NUM_12BITS; + } +} + +static inline enum mtk_dpi_out_channel_swap +mtk_dpi_bus_fmt_channel_swap(unsigned int out_bus_format) +{ + switch (out_bus_format) { + default: + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB888_2X12_LE: + case MEDIA_BUS_FMT_RGB888_2X12_BE: + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YUYV10_1X20: + case MEDIA_BUS_FMT_YUYV12_1X24: + return MTK_DPI_OUT_CHANNEL_SWAP_RGB; + case MEDIA_BUS_FMT_BGR888_1X24: + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + return MTK_DPI_OUT_CHANNEL_SWAP_BGR; + } +} + +static inline enum mtk_dpi_out_color_format +mtk_dpi_bus_fmt_color_format(unsigned int out_bus_format) +{ + switch (out_bus_format) { + default: + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_BGR888_1X24: + case MEDIA_BUS_FMT_RGB888_2X12_LE: + case MEDIA_BUS_FMT_RGB888_2X12_BE: + case MEDIA_BUS_FMT_RGB101010_1X30: + return MTK_DPI_COLOR_FORMAT_RGB; + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YUYV10_1X20: + case MEDIA_BUS_FMT_YUYV12_1X24: + return MTK_DPI_COLOR_FORMAT_YCBCR_422; + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + return MTK_DPI_COLOR_FORMAT_YCBCR_444; + } +} + +u32 *mtk_dpi_bridge_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); +u32 *mtk_dpi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + unsigned int *num_output_fmts); +int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state); +void mtk_dpi_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode); + +int mtk_dpi_common_bind(struct device *dev, struct device *master, void *data); +void mtk_dpi_common_unbind(struct device *dev, struct device *master, void *data); + +void mtk_dpi_power_off(struct mtk_dpi *dpi); +int mtk_dpi_power_on(struct mtk_dpi *dpi); +int mtk_dpi_set_display_mode(struct mtk_dpi *dpi, struct videomode *vm, + struct mtk_dpi_sync *sync, + struct mtk_dpi_polarities *dpi_pol, + struct mtk_dpi_yc_limit *limit); + +int mtk_dpi_common_debug_tp_show(struct seq_file *m, bool en, u32 sel); +ssize_t mtk_dpi_common_debug_tp_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp, u32 *en, u32 *type); + +struct mtk_dpi *mtk_dpi_common_probe(struct platform_device *pdev, + const struct drm_bridge_funcs *bridge_funcs); + +#endif /* _MTK_DPI_COMMON_H */ -- 2.54.0
