444 lines · c
1// SPDX-License-Identifier: GPL-2.0+2 3/*4 * Copyright 2020 NXP5 */6 7#include <linux/bitfield.h>8#include <linux/clk.h>9#include <linux/delay.h>10#include <linux/io.h>11#include <linux/media-bus-format.h>12#include <linux/module.h>13#include <linux/of.h>14#include <linux/of_graph.h>15#include <linux/platform_device.h>16#include <linux/pm_runtime.h>17 18#include <drm/drm_atomic_state_helper.h>19#include <drm/drm_bridge.h>20#include <drm/drm_print.h>21 22#define PC_CTRL_REG 0x023#define PC_COMBINE_ENABLE BIT(0)24#define PC_DISP_BYPASS(n) BIT(1 + 21 * (n))25#define PC_DISP_HSYNC_POLARITY(n) BIT(2 + 11 * (n))26#define PC_DISP_HSYNC_POLARITY_POS(n) DISP_HSYNC_POLARITY(n)27#define PC_DISP_VSYNC_POLARITY(n) BIT(3 + 11 * (n))28#define PC_DISP_VSYNC_POLARITY_POS(n) DISP_VSYNC_POLARITY(n)29#define PC_DISP_DVALID_POLARITY(n) BIT(4 + 11 * (n))30#define PC_DISP_DVALID_POLARITY_POS(n) DISP_DVALID_POLARITY(n)31#define PC_VSYNC_MASK_ENABLE BIT(5)32#define PC_SKIP_MODE BIT(6)33#define PC_SKIP_NUMBER_MASK GENMASK(12, 7)34#define PC_SKIP_NUMBER(n) FIELD_PREP(PC_SKIP_NUMBER_MASK, (n))35#define PC_DISP0_PIX_DATA_FORMAT_MASK GENMASK(18, 16)36#define PC_DISP0_PIX_DATA_FORMAT(fmt) \37 FIELD_PREP(PC_DISP0_PIX_DATA_FORMAT_MASK, (fmt))38#define PC_DISP1_PIX_DATA_FORMAT_MASK GENMASK(21, 19)39#define PC_DISP1_PIX_DATA_FORMAT(fmt) \40 FIELD_PREP(PC_DISP1_PIX_DATA_FORMAT_MASK, (fmt))41 42#define PC_SW_RESET_REG 0x2043#define PC_SW_RESET_N BIT(0)44#define PC_DISP_SW_RESET_N(n) BIT(1 + (n))45#define PC_FULL_RESET_N (PC_SW_RESET_N | \46 PC_DISP_SW_RESET_N(0) | \47 PC_DISP_SW_RESET_N(1))48 49#define PC_REG_SET 0x450#define PC_REG_CLR 0x851 52#define DRIVER_NAME "imx8qxp-pixel-combiner"53 54enum imx8qxp_pc_pix_data_format {55 RGB,56 YUV444,57 YUV422,58 SPLIT_RGB,59};60 61struct imx8qxp_pc_channel {62 struct drm_bridge bridge;63 struct drm_bridge *next_bridge;64 struct imx8qxp_pc *pc;65 unsigned int stream_id;66 bool is_available;67};68 69struct imx8qxp_pc {70 struct device *dev;71 struct imx8qxp_pc_channel ch[2];72 struct clk *clk_apb;73 void __iomem *base;74};75 76static inline u32 imx8qxp_pc_read(struct imx8qxp_pc *pc, unsigned int offset)77{78 return readl(pc->base + offset);79}80 81static inline void82imx8qxp_pc_write(struct imx8qxp_pc *pc, unsigned int offset, u32 value)83{84 writel(value, pc->base + offset);85}86 87static inline void88imx8qxp_pc_write_set(struct imx8qxp_pc *pc, unsigned int offset, u32 value)89{90 imx8qxp_pc_write(pc, offset + PC_REG_SET, value);91}92 93static inline void94imx8qxp_pc_write_clr(struct imx8qxp_pc *pc, unsigned int offset, u32 value)95{96 imx8qxp_pc_write(pc, offset + PC_REG_CLR, value);97}98 99static enum drm_mode_status100imx8qxp_pc_bridge_mode_valid(struct drm_bridge *bridge,101 const struct drm_display_info *info,102 const struct drm_display_mode *mode)103{104 if (mode->hdisplay > 2560)105 return MODE_BAD_HVALUE;106 107 return MODE_OK;108}109 110static int imx8qxp_pc_bridge_attach(struct drm_bridge *bridge,111 enum drm_bridge_attach_flags flags)112{113 struct imx8qxp_pc_channel *ch = bridge->driver_private;114 struct imx8qxp_pc *pc = ch->pc;115 116 if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {117 DRM_DEV_ERROR(pc->dev,118 "do not support creating a drm_connector\n");119 return -EINVAL;120 }121 122 return drm_bridge_attach(bridge->encoder,123 ch->next_bridge, bridge,124 DRM_BRIDGE_ATTACH_NO_CONNECTOR);125}126 127static void128imx8qxp_pc_bridge_mode_set(struct drm_bridge *bridge,129 const struct drm_display_mode *mode,130 const struct drm_display_mode *adjusted_mode)131{132 struct imx8qxp_pc_channel *ch = bridge->driver_private;133 struct imx8qxp_pc *pc = ch->pc;134 u32 val;135 int ret;136 137 ret = pm_runtime_get_sync(pc->dev);138 if (ret < 0)139 DRM_DEV_ERROR(pc->dev,140 "failed to get runtime PM sync: %d\n", ret);141 142 ret = clk_prepare_enable(pc->clk_apb);143 if (ret)144 DRM_DEV_ERROR(pc->dev, "%s: failed to enable apb clock: %d\n",145 __func__, ret);146 147 /* HSYNC to pixel link is active low. */148 imx8qxp_pc_write_clr(pc, PC_CTRL_REG,149 PC_DISP_HSYNC_POLARITY(ch->stream_id));150 151 /* VSYNC to pixel link is active low. */152 imx8qxp_pc_write_clr(pc, PC_CTRL_REG,153 PC_DISP_VSYNC_POLARITY(ch->stream_id));154 155 /* Data enable to pixel link is active high. */156 imx8qxp_pc_write_set(pc, PC_CTRL_REG,157 PC_DISP_DVALID_POLARITY(ch->stream_id));158 159 /* Mask the first frame output which may be incomplete. */160 imx8qxp_pc_write_set(pc, PC_CTRL_REG, PC_VSYNC_MASK_ENABLE);161 162 /* Only support RGB currently. */163 val = imx8qxp_pc_read(pc, PC_CTRL_REG);164 if (ch->stream_id == 0) {165 val &= ~PC_DISP0_PIX_DATA_FORMAT_MASK;166 val |= PC_DISP0_PIX_DATA_FORMAT(RGB);167 } else {168 val &= ~PC_DISP1_PIX_DATA_FORMAT_MASK;169 val |= PC_DISP1_PIX_DATA_FORMAT(RGB);170 }171 imx8qxp_pc_write(pc, PC_CTRL_REG, val);172 173 /* Only support bypass mode currently. */174 imx8qxp_pc_write_set(pc, PC_CTRL_REG, PC_DISP_BYPASS(ch->stream_id));175 176 clk_disable_unprepare(pc->clk_apb);177}178 179static void180imx8qxp_pc_bridge_atomic_disable(struct drm_bridge *bridge,181 struct drm_bridge_state *old_bridge_state)182{183 struct imx8qxp_pc_channel *ch = bridge->driver_private;184 struct imx8qxp_pc *pc = ch->pc;185 int ret;186 187 ret = pm_runtime_put(pc->dev);188 if (ret < 0)189 DRM_DEV_ERROR(pc->dev, "failed to put runtime PM: %d\n", ret);190}191 192static const u32 imx8qxp_pc_bus_output_fmts[] = {193 MEDIA_BUS_FMT_RGB888_1X36_CPADLO,194 MEDIA_BUS_FMT_RGB666_1X36_CPADLO,195};196 197static bool imx8qxp_pc_bus_output_fmt_supported(u32 fmt)198{199 int i;200 201 for (i = 0; i < ARRAY_SIZE(imx8qxp_pc_bus_output_fmts); i++) {202 if (imx8qxp_pc_bus_output_fmts[i] == fmt)203 return true;204 }205 206 return false;207}208 209static u32 *210imx8qxp_pc_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,211 struct drm_bridge_state *bridge_state,212 struct drm_crtc_state *crtc_state,213 struct drm_connector_state *conn_state,214 u32 output_fmt,215 unsigned int *num_input_fmts)216{217 u32 *input_fmts;218 219 if (!imx8qxp_pc_bus_output_fmt_supported(output_fmt))220 return NULL;221 222 *num_input_fmts = 1;223 224 input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);225 if (!input_fmts)226 return NULL;227 228 switch (output_fmt) {229 case MEDIA_BUS_FMT_RGB888_1X36_CPADLO:230 input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X30_CPADLO;231 break;232 case MEDIA_BUS_FMT_RGB666_1X36_CPADLO:233 input_fmts[0] = MEDIA_BUS_FMT_RGB666_1X30_CPADLO;234 break;235 default:236 kfree(input_fmts);237 input_fmts = NULL;238 break;239 }240 241 return input_fmts;242}243 244static u32 *245imx8qxp_pc_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,246 struct drm_bridge_state *bridge_state,247 struct drm_crtc_state *crtc_state,248 struct drm_connector_state *conn_state,249 unsigned int *num_output_fmts)250{251 *num_output_fmts = ARRAY_SIZE(imx8qxp_pc_bus_output_fmts);252 return kmemdup(imx8qxp_pc_bus_output_fmts,253 sizeof(imx8qxp_pc_bus_output_fmts), GFP_KERNEL);254}255 256static const struct drm_bridge_funcs imx8qxp_pc_bridge_funcs = {257 .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,258 .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,259 .atomic_reset = drm_atomic_helper_bridge_reset,260 .mode_valid = imx8qxp_pc_bridge_mode_valid,261 .attach = imx8qxp_pc_bridge_attach,262 .mode_set = imx8qxp_pc_bridge_mode_set,263 .atomic_disable = imx8qxp_pc_bridge_atomic_disable,264 .atomic_get_input_bus_fmts =265 imx8qxp_pc_bridge_atomic_get_input_bus_fmts,266 .atomic_get_output_bus_fmts =267 imx8qxp_pc_bridge_atomic_get_output_bus_fmts,268};269 270static int imx8qxp_pc_bridge_probe(struct platform_device *pdev)271{272 struct imx8qxp_pc *pc;273 struct imx8qxp_pc_channel *ch;274 struct device *dev = &pdev->dev;275 struct device_node *np = dev->of_node;276 struct device_node *child, *remote;277 u32 i;278 int ret;279 280 pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);281 if (!pc)282 return -ENOMEM;283 284 pc->base = devm_platform_ioremap_resource(pdev, 0);285 if (IS_ERR(pc->base))286 return PTR_ERR(pc->base);287 288 pc->dev = dev;289 290 pc->clk_apb = devm_clk_get(dev, "apb");291 if (IS_ERR(pc->clk_apb)) {292 ret = PTR_ERR(pc->clk_apb);293 if (ret != -EPROBE_DEFER)294 DRM_DEV_ERROR(dev, "failed to get apb clock: %d\n", ret);295 return ret;296 }297 298 platform_set_drvdata(pdev, pc);299 pm_runtime_enable(dev);300 301 for_each_available_child_of_node(np, child) {302 ret = of_property_read_u32(child, "reg", &i);303 if (ret || i > 1) {304 ret = -EINVAL;305 DRM_DEV_ERROR(dev,306 "invalid channel(%u) node address\n", i);307 goto free_child;308 }309 310 ch = &pc->ch[i];311 ch->pc = pc;312 ch->stream_id = i;313 314 remote = of_graph_get_remote_node(child, 1, 0);315 if (!remote) {316 ret = -ENODEV;317 DRM_DEV_ERROR(dev,318 "channel%u failed to get port1's remote node: %d\n",319 i, ret);320 goto free_child;321 }322 323 ch->next_bridge = of_drm_find_bridge(remote);324 if (!ch->next_bridge) {325 of_node_put(remote);326 ret = -EPROBE_DEFER;327 DRM_DEV_DEBUG_DRIVER(dev,328 "channel%u failed to find next bridge: %d\n",329 i, ret);330 goto free_child;331 }332 333 of_node_put(remote);334 335 ch->bridge.driver_private = ch;336 ch->bridge.funcs = &imx8qxp_pc_bridge_funcs;337 ch->bridge.of_node = child;338 ch->is_available = true;339 340 drm_bridge_add(&ch->bridge);341 }342 343 return 0;344 345free_child:346 of_node_put(child);347 348 if (i == 1 && pc->ch[0].next_bridge)349 drm_bridge_remove(&pc->ch[0].bridge);350 351 pm_runtime_disable(dev);352 return ret;353}354 355static void imx8qxp_pc_bridge_remove(struct platform_device *pdev)356{357 struct imx8qxp_pc *pc = platform_get_drvdata(pdev);358 struct imx8qxp_pc_channel *ch;359 int i;360 361 for (i = 0; i < 2; i++) {362 ch = &pc->ch[i];363 364 if (!ch->is_available)365 continue;366 367 drm_bridge_remove(&ch->bridge);368 ch->is_available = false;369 }370 371 pm_runtime_disable(&pdev->dev);372}373 374static int __maybe_unused imx8qxp_pc_runtime_suspend(struct device *dev)375{376 struct platform_device *pdev = to_platform_device(dev);377 struct imx8qxp_pc *pc = platform_get_drvdata(pdev);378 int ret;379 380 ret = clk_prepare_enable(pc->clk_apb);381 if (ret)382 DRM_DEV_ERROR(pc->dev, "%s: failed to enable apb clock: %d\n",383 __func__, ret);384 385 /* Disable pixel combiner by full reset. */386 imx8qxp_pc_write_clr(pc, PC_SW_RESET_REG, PC_FULL_RESET_N);387 388 clk_disable_unprepare(pc->clk_apb);389 390 /* Ensure the reset takes effect. */391 usleep_range(10, 20);392 393 return ret;394}395 396static int __maybe_unused imx8qxp_pc_runtime_resume(struct device *dev)397{398 struct platform_device *pdev = to_platform_device(dev);399 struct imx8qxp_pc *pc = platform_get_drvdata(pdev);400 int ret;401 402 ret = clk_prepare_enable(pc->clk_apb);403 if (ret) {404 DRM_DEV_ERROR(pc->dev, "%s: failed to enable apb clock: %d\n",405 __func__, ret);406 return ret;407 }408 409 /* out of reset */410 imx8qxp_pc_write_set(pc, PC_SW_RESET_REG, PC_FULL_RESET_N);411 412 clk_disable_unprepare(pc->clk_apb);413 414 return ret;415}416 417static const struct dev_pm_ops imx8qxp_pc_pm_ops = {418 SET_RUNTIME_PM_OPS(imx8qxp_pc_runtime_suspend,419 imx8qxp_pc_runtime_resume, NULL)420};421 422static const struct of_device_id imx8qxp_pc_dt_ids[] = {423 { .compatible = "fsl,imx8qm-pixel-combiner", },424 { .compatible = "fsl,imx8qxp-pixel-combiner", },425 { /* sentinel */ }426};427MODULE_DEVICE_TABLE(of, imx8qxp_pc_dt_ids);428 429static struct platform_driver imx8qxp_pc_bridge_driver = {430 .probe = imx8qxp_pc_bridge_probe,431 .remove_new = imx8qxp_pc_bridge_remove,432 .driver = {433 .pm = &imx8qxp_pc_pm_ops,434 .name = DRIVER_NAME,435 .of_match_table = imx8qxp_pc_dt_ids,436 },437};438module_platform_driver(imx8qxp_pc_bridge_driver);439 440MODULE_DESCRIPTION("i.MX8QM/QXP pixel combiner bridge driver");441MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");442MODULE_LICENSE("GPL v2");443MODULE_ALIAS("platform:" DRIVER_NAME);444