brintos

brintos / linux-shallow public Read only

0
0
Text · 15.9 KiB · 21471a9 Raw
587 lines · c
1// SPDX-License-Identifier: GPL-2.0+2 3/*4 * Copyright 2020 NXP5 */6 7#include <linux/clk.h>8#include <linux/media-bus-format.h>9#include <linux/mfd/syscon.h>10#include <linux/module.h>11#include <linux/of.h>12#include <linux/of_graph.h>13#include <linux/phy/phy.h>14#include <linux/platform_device.h>15#include <linux/pm_runtime.h>16#include <linux/regmap.h>17 18#include <drm/drm_atomic_state_helper.h>19#include <drm/drm_bridge.h>20#include <drm/drm_connector.h>21#include <drm/drm_fourcc.h>22#include <drm/drm_of.h>23#include <drm/drm_print.h>24 25#include "imx-ldb-helper.h"26 27#define  LDB_CH0_10BIT_EN		BIT(22)28#define  LDB_CH1_10BIT_EN		BIT(23)29#define  LDB_CH0_DATA_WIDTH_24BIT	BIT(24)30#define  LDB_CH1_DATA_WIDTH_24BIT	BIT(26)31#define  LDB_CH0_DATA_WIDTH_30BIT	(2 << 24)32#define  LDB_CH1_DATA_WIDTH_30BIT	(2 << 26)33 34#define SS_CTRL				0x2035#define  CH_HSYNC_M(id)			BIT(0 + ((id) * 2))36#define  CH_VSYNC_M(id)			BIT(1 + ((id) * 2))37#define  CH_PHSYNC(id)			BIT(0 + ((id) * 2))38#define  CH_PVSYNC(id)			BIT(1 + ((id) * 2))39 40#define DRIVER_NAME			"imx8qm-ldb"41 42struct imx8qm_ldb_channel {43	struct ldb_channel base;44	struct phy *phy;45};46 47struct imx8qm_ldb {48	struct ldb base;49	struct device *dev;50	struct imx8qm_ldb_channel channel[MAX_LDB_CHAN_NUM];51	struct clk *clk_pixel;52	struct clk *clk_bypass;53	int active_chno;54};55 56static inline struct imx8qm_ldb_channel *57base_to_imx8qm_ldb_channel(struct ldb_channel *base)58{59	return container_of(base, struct imx8qm_ldb_channel, base);60}61 62static inline struct imx8qm_ldb *base_to_imx8qm_ldb(struct ldb *base)63{64	return container_of(base, struct imx8qm_ldb, base);65}66 67static void imx8qm_ldb_set_phy_cfg(struct imx8qm_ldb *imx8qm_ldb,68				   unsigned long di_clk,69				   bool is_split, bool is_slave,70				   struct phy_configure_opts_lvds *phy_cfg)71{72	phy_cfg->bits_per_lane_and_dclk_cycle = 7;73	phy_cfg->lanes = 4;74	phy_cfg->differential_clk_rate = is_split ? di_clk / 2 : di_clk;75	phy_cfg->is_slave = is_slave;76}77 78static int imx8qm_ldb_bridge_atomic_check(struct drm_bridge *bridge,79					  struct drm_bridge_state *bridge_state,80					  struct drm_crtc_state *crtc_state,81					  struct drm_connector_state *conn_state)82{83	struct ldb_channel *ldb_ch = bridge->driver_private;84	struct ldb *ldb = ldb_ch->ldb;85	struct imx8qm_ldb_channel *imx8qm_ldb_ch =86					base_to_imx8qm_ldb_channel(ldb_ch);87	struct imx8qm_ldb *imx8qm_ldb = base_to_imx8qm_ldb(ldb);88	struct drm_display_mode *adj = &crtc_state->adjusted_mode;89	unsigned long di_clk = adj->clock * 1000;90	bool is_split = ldb_channel_is_split_link(ldb_ch);91	union phy_configure_opts opts = { };92	struct phy_configure_opts_lvds *phy_cfg = &opts.lvds;93	int ret;94 95	ret = ldb_bridge_atomic_check_helper(bridge, bridge_state,96					     crtc_state, conn_state);97	if (ret)98		return ret;99 100	imx8qm_ldb_set_phy_cfg(imx8qm_ldb, di_clk, is_split, false, phy_cfg);101	ret = phy_validate(imx8qm_ldb_ch->phy, PHY_MODE_LVDS, 0, &opts);102	if (ret < 0) {103		DRM_DEV_DEBUG_DRIVER(imx8qm_ldb->dev,104				     "failed to validate PHY: %d\n", ret);105		return ret;106	}107 108	if (is_split) {109		imx8qm_ldb_ch =110			&imx8qm_ldb->channel[imx8qm_ldb->active_chno ^ 1];111		imx8qm_ldb_set_phy_cfg(imx8qm_ldb, di_clk, is_split, true,112				       phy_cfg);113		ret = phy_validate(imx8qm_ldb_ch->phy, PHY_MODE_LVDS, 0, &opts);114		if (ret < 0) {115			DRM_DEV_DEBUG_DRIVER(imx8qm_ldb->dev,116					     "failed to validate slave PHY: %d\n",117					     ret);118			return ret;119		}120	}121 122	return ret;123}124 125static void126imx8qm_ldb_bridge_mode_set(struct drm_bridge *bridge,127			   const struct drm_display_mode *mode,128			   const struct drm_display_mode *adjusted_mode)129{130	struct ldb_channel *ldb_ch = bridge->driver_private;131	struct ldb *ldb = ldb_ch->ldb;132	struct imx8qm_ldb_channel *imx8qm_ldb_ch =133					base_to_imx8qm_ldb_channel(ldb_ch);134	struct imx8qm_ldb *imx8qm_ldb = base_to_imx8qm_ldb(ldb);135	struct device *dev = imx8qm_ldb->dev;136	unsigned long di_clk = adjusted_mode->clock * 1000;137	bool is_split = ldb_channel_is_split_link(ldb_ch);138	union phy_configure_opts opts = { };139	struct phy_configure_opts_lvds *phy_cfg = &opts.lvds;140	u32 chno = ldb_ch->chno;141	int ret;142 143	ret = pm_runtime_get_sync(dev);144	if (ret < 0)145		DRM_DEV_ERROR(dev, "failed to get runtime PM sync: %d\n", ret);146 147	ret = phy_init(imx8qm_ldb_ch->phy);148	if (ret < 0)149		DRM_DEV_ERROR(dev, "failed to initialize PHY: %d\n", ret);150 151	clk_set_rate(imx8qm_ldb->clk_bypass, di_clk);152	clk_set_rate(imx8qm_ldb->clk_pixel, di_clk);153 154	imx8qm_ldb_set_phy_cfg(imx8qm_ldb, di_clk, is_split, false, phy_cfg);155	ret = phy_configure(imx8qm_ldb_ch->phy, &opts);156	if (ret < 0)157		DRM_DEV_ERROR(dev, "failed to configure PHY: %d\n", ret);158 159	if (is_split) {160		imx8qm_ldb_ch =161			&imx8qm_ldb->channel[imx8qm_ldb->active_chno ^ 1];162		imx8qm_ldb_set_phy_cfg(imx8qm_ldb, di_clk, is_split, true,163				       phy_cfg);164		ret = phy_configure(imx8qm_ldb_ch->phy, &opts);165		if (ret < 0)166			DRM_DEV_ERROR(dev, "failed to configure slave PHY: %d\n",167				      ret);168	}169 170	/* input VSYNC signal from pixel link is active low */171	if (ldb_ch->chno == 0 || is_split)172		ldb->ldb_ctrl |= LDB_DI0_VS_POL_ACT_LOW;173	if (ldb_ch->chno == 1 || is_split)174		ldb->ldb_ctrl |= LDB_DI1_VS_POL_ACT_LOW;175 176	switch (ldb_ch->out_bus_format) {177	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:178		break;179	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:180	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:181		if (ldb_ch->chno == 0 || is_split)182			ldb->ldb_ctrl |= LDB_CH0_DATA_WIDTH_24BIT;183		if (ldb_ch->chno == 1 || is_split)184			ldb->ldb_ctrl |= LDB_CH1_DATA_WIDTH_24BIT;185		break;186	}187 188	ldb_bridge_mode_set_helper(bridge, mode, adjusted_mode);189 190	if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)191		regmap_update_bits(ldb->regmap, SS_CTRL, CH_VSYNC_M(chno), 0);192	else if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)193		regmap_update_bits(ldb->regmap, SS_CTRL,194				   CH_VSYNC_M(chno), CH_PVSYNC(chno));195 196	if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)197		regmap_update_bits(ldb->regmap, SS_CTRL, CH_HSYNC_M(chno), 0);198	else if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)199		regmap_update_bits(ldb->regmap, SS_CTRL,200				   CH_HSYNC_M(chno), CH_PHSYNC(chno));201}202 203static void204imx8qm_ldb_bridge_atomic_enable(struct drm_bridge *bridge,205				struct drm_bridge_state *old_bridge_state)206{207	struct ldb_channel *ldb_ch = bridge->driver_private;208	struct ldb *ldb = ldb_ch->ldb;209	struct imx8qm_ldb_channel *imx8qm_ldb_ch =210					base_to_imx8qm_ldb_channel(ldb_ch);211	struct imx8qm_ldb *imx8qm_ldb = base_to_imx8qm_ldb(ldb);212	struct device *dev = imx8qm_ldb->dev;213	bool is_split = ldb_channel_is_split_link(ldb_ch);214	int ret;215 216	clk_prepare_enable(imx8qm_ldb->clk_pixel);217	clk_prepare_enable(imx8qm_ldb->clk_bypass);218 219	/* both DI0 and DI1 connect with pixel link, so ok to use DI0 only */220	if (ldb_ch->chno == 0 || is_split) {221		ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;222		ldb->ldb_ctrl |= LDB_CH0_MODE_EN_TO_DI0;223	}224	if (ldb_ch->chno == 1 || is_split) {225		ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK;226		ldb->ldb_ctrl |= LDB_CH1_MODE_EN_TO_DI0;227	}228 229	if (is_split) {230		ret = phy_power_on(imx8qm_ldb->channel[0].phy);231		if (ret)232			DRM_DEV_ERROR(dev,233				      "failed to power on channel0 PHY: %d\n",234				      ret);235 236		ret = phy_power_on(imx8qm_ldb->channel[1].phy);237		if (ret)238			DRM_DEV_ERROR(dev,239				      "failed to power on channel1 PHY: %d\n",240				      ret);241	} else {242		ret = phy_power_on(imx8qm_ldb_ch->phy);243		if (ret)244			DRM_DEV_ERROR(dev, "failed to power on PHY: %d\n", ret);245	}246 247	ldb_bridge_enable_helper(bridge);248}249 250static void251imx8qm_ldb_bridge_atomic_disable(struct drm_bridge *bridge,252				 struct drm_bridge_state *old_bridge_state)253{254	struct ldb_channel *ldb_ch = bridge->driver_private;255	struct ldb *ldb = ldb_ch->ldb;256	struct imx8qm_ldb_channel *imx8qm_ldb_ch =257					base_to_imx8qm_ldb_channel(ldb_ch);258	struct imx8qm_ldb *imx8qm_ldb = base_to_imx8qm_ldb(ldb);259	struct device *dev = imx8qm_ldb->dev;260	bool is_split = ldb_channel_is_split_link(ldb_ch);261	int ret;262 263	ldb_bridge_disable_helper(bridge);264 265	if (is_split) {266		ret = phy_power_off(imx8qm_ldb->channel[0].phy);267		if (ret)268			DRM_DEV_ERROR(dev,269				      "failed to power off channel0 PHY: %d\n",270				      ret);271		ret = phy_power_off(imx8qm_ldb->channel[1].phy);272		if (ret)273			DRM_DEV_ERROR(dev,274				      "failed to power off channel1 PHY: %d\n",275				      ret);276	} else {277		ret = phy_power_off(imx8qm_ldb_ch->phy);278		if (ret)279			DRM_DEV_ERROR(dev, "failed to power off PHY: %d\n", ret);280	}281 282	clk_disable_unprepare(imx8qm_ldb->clk_bypass);283	clk_disable_unprepare(imx8qm_ldb->clk_pixel);284 285	ret = pm_runtime_put(dev);286	if (ret < 0)287		DRM_DEV_ERROR(dev, "failed to put runtime PM: %d\n", ret);288}289 290static const u32 imx8qm_ldb_bus_output_fmts[] = {291	MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,292	MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,293	MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,294	MEDIA_BUS_FMT_FIXED,295};296 297static bool imx8qm_ldb_bus_output_fmt_supported(u32 fmt)298{299	int i;300 301	for (i = 0; i < ARRAY_SIZE(imx8qm_ldb_bus_output_fmts); i++) {302		if (imx8qm_ldb_bus_output_fmts[i] == fmt)303			return true;304	}305 306	return false;307}308 309static u32 *310imx8qm_ldb_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,311					    struct drm_bridge_state *bridge_state,312					    struct drm_crtc_state *crtc_state,313					    struct drm_connector_state *conn_state,314					    u32 output_fmt,315					    unsigned int *num_input_fmts)316{317	struct drm_display_info *di;318	const struct drm_format_info *finfo;319	u32 *input_fmts;320 321	if (!imx8qm_ldb_bus_output_fmt_supported(output_fmt))322		return NULL;323 324	*num_input_fmts = 1;325 326	input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);327	if (!input_fmts)328		return NULL;329 330	switch (output_fmt) {331	case MEDIA_BUS_FMT_FIXED:332		di = &conn_state->connector->display_info;333 334		/*335		 * Look at the first bus format to determine input format.336		 * Default to MEDIA_BUS_FMT_RGB888_1X36_CPADLO, if no match.337		 */338		if (di->num_bus_formats) {339			finfo = drm_format_info(di->bus_formats[0]);340 341			input_fmts[0] = finfo->depth == 18 ?342					MEDIA_BUS_FMT_RGB666_1X36_CPADLO :343					MEDIA_BUS_FMT_RGB888_1X36_CPADLO;344		} else {345			input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X36_CPADLO;346		}347		break;348	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:349		input_fmts[0] = MEDIA_BUS_FMT_RGB666_1X36_CPADLO;350		break;351	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:352	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:353		input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X36_CPADLO;354		break;355	default:356		kfree(input_fmts);357		input_fmts = NULL;358		break;359	}360 361	return input_fmts;362}363 364static u32 *365imx8qm_ldb_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,366					     struct drm_bridge_state *bridge_state,367					     struct drm_crtc_state *crtc_state,368					     struct drm_connector_state *conn_state,369					     unsigned int *num_output_fmts)370{371	*num_output_fmts = ARRAY_SIZE(imx8qm_ldb_bus_output_fmts);372	return kmemdup(imx8qm_ldb_bus_output_fmts,373			sizeof(imx8qm_ldb_bus_output_fmts), GFP_KERNEL);374}375 376static enum drm_mode_status377imx8qm_ldb_bridge_mode_valid(struct drm_bridge *bridge,378			     const struct drm_display_info *info,379			     const struct drm_display_mode *mode)380{381	struct ldb_channel *ldb_ch = bridge->driver_private;382	bool is_single = ldb_channel_is_single_link(ldb_ch);383 384	if (mode->clock > 300000)385		return MODE_CLOCK_HIGH;386 387	if (mode->clock > 150000 && is_single)388		return MODE_CLOCK_HIGH;389 390	return MODE_OK;391}392 393static const struct drm_bridge_funcs imx8qm_ldb_bridge_funcs = {394	.atomic_duplicate_state	= drm_atomic_helper_bridge_duplicate_state,395	.atomic_destroy_state	= drm_atomic_helper_bridge_destroy_state,396	.atomic_reset		= drm_atomic_helper_bridge_reset,397	.mode_valid		= imx8qm_ldb_bridge_mode_valid,398	.attach			= ldb_bridge_attach_helper,399	.atomic_check		= imx8qm_ldb_bridge_atomic_check,400	.mode_set		= imx8qm_ldb_bridge_mode_set,401	.atomic_enable		= imx8qm_ldb_bridge_atomic_enable,402	.atomic_disable		= imx8qm_ldb_bridge_atomic_disable,403	.atomic_get_input_bus_fmts =404			imx8qm_ldb_bridge_atomic_get_input_bus_fmts,405	.atomic_get_output_bus_fmts =406			imx8qm_ldb_bridge_atomic_get_output_bus_fmts,407};408 409static int imx8qm_ldb_get_phy(struct imx8qm_ldb *imx8qm_ldb)410{411	struct imx8qm_ldb_channel *imx8qm_ldb_ch;412	struct ldb_channel *ldb_ch;413	struct device *dev = imx8qm_ldb->dev;414	int i, ret;415 416	for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {417		imx8qm_ldb_ch = &imx8qm_ldb->channel[i];418		ldb_ch = &imx8qm_ldb_ch->base;419 420		if (!ldb_ch->is_available)421			continue;422 423		imx8qm_ldb_ch->phy = devm_of_phy_get(dev, ldb_ch->np,424						     "lvds_phy");425		if (IS_ERR(imx8qm_ldb_ch->phy)) {426			ret = PTR_ERR(imx8qm_ldb_ch->phy);427			if (ret != -EPROBE_DEFER)428				DRM_DEV_ERROR(dev,429					      "failed to get channel%d PHY: %d\n",430					      i, ret);431			return ret;432		}433	}434 435	return 0;436}437 438static int imx8qm_ldb_probe(struct platform_device *pdev)439{440	struct device *dev = &pdev->dev;441	struct imx8qm_ldb *imx8qm_ldb;442	struct imx8qm_ldb_channel *imx8qm_ldb_ch;443	struct ldb *ldb;444	struct ldb_channel *ldb_ch;445	struct device_node *port1, *port2;446	int pixel_order;447	int ret, i;448 449	imx8qm_ldb = devm_kzalloc(dev, sizeof(*imx8qm_ldb), GFP_KERNEL);450	if (!imx8qm_ldb)451		return -ENOMEM;452 453	imx8qm_ldb->clk_pixel = devm_clk_get(dev, "pixel");454	if (IS_ERR(imx8qm_ldb->clk_pixel)) {455		ret = PTR_ERR(imx8qm_ldb->clk_pixel);456		if (ret != -EPROBE_DEFER)457			DRM_DEV_ERROR(dev,458				      "failed to get pixel clock: %d\n", ret);459		return ret;460	}461 462	imx8qm_ldb->clk_bypass = devm_clk_get(dev, "bypass");463	if (IS_ERR(imx8qm_ldb->clk_bypass)) {464		ret = PTR_ERR(imx8qm_ldb->clk_bypass);465		if (ret != -EPROBE_DEFER)466			DRM_DEV_ERROR(dev,467				      "failed to get bypass clock: %d\n", ret);468		return ret;469	}470 471	imx8qm_ldb->dev = dev;472 473	ldb = &imx8qm_ldb->base;474	ldb->dev = dev;475	ldb->ctrl_reg = 0xe0;476 477	for (i = 0; i < MAX_LDB_CHAN_NUM; i++)478		ldb->channel[i] = &imx8qm_ldb->channel[i].base;479 480	ret = ldb_init_helper(ldb);481	if (ret)482		return ret;483 484	if (ldb->available_ch_cnt == 0) {485		DRM_DEV_DEBUG_DRIVER(dev, "no available channel\n");486		return 0;487	}488 489	if (ldb->available_ch_cnt == 2) {490		port1 = of_graph_get_port_by_id(ldb->channel[0]->np, 1);491		port2 = of_graph_get_port_by_id(ldb->channel[1]->np, 1);492		pixel_order =493			drm_of_lvds_get_dual_link_pixel_order(port1, port2);494		of_node_put(port1);495		of_node_put(port2);496 497		if (pixel_order != DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS) {498			DRM_DEV_ERROR(dev, "invalid dual link pixel order: %d\n",499				      pixel_order);500			return -EINVAL;501		}502 503		imx8qm_ldb->active_chno = 0;504		imx8qm_ldb_ch = &imx8qm_ldb->channel[0];505		ldb_ch = &imx8qm_ldb_ch->base;506		ldb_ch->link_type = pixel_order;507	} else {508		for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {509			imx8qm_ldb_ch = &imx8qm_ldb->channel[i];510			ldb_ch = &imx8qm_ldb_ch->base;511 512			if (ldb_ch->is_available) {513				imx8qm_ldb->active_chno = ldb_ch->chno;514				break;515			}516		}517	}518 519	ret = imx8qm_ldb_get_phy(imx8qm_ldb);520	if (ret)521		return ret;522 523	ret = ldb_find_next_bridge_helper(ldb);524	if (ret)525		return ret;526 527	platform_set_drvdata(pdev, imx8qm_ldb);528	pm_runtime_enable(dev);529 530	ldb_add_bridge_helper(ldb, &imx8qm_ldb_bridge_funcs);531 532	return ret;533}534 535static void imx8qm_ldb_remove(struct platform_device *pdev)536{537	struct imx8qm_ldb *imx8qm_ldb = platform_get_drvdata(pdev);538	struct ldb *ldb = &imx8qm_ldb->base;539 540	ldb_remove_bridge_helper(ldb);541 542	pm_runtime_disable(&pdev->dev);543}544 545static int __maybe_unused imx8qm_ldb_runtime_suspend(struct device *dev)546{547	return 0;548}549 550static int __maybe_unused imx8qm_ldb_runtime_resume(struct device *dev)551{552	struct imx8qm_ldb *imx8qm_ldb = dev_get_drvdata(dev);553	struct ldb *ldb = &imx8qm_ldb->base;554 555	/* disable LDB by resetting the control register to POR default */556	regmap_write(ldb->regmap, ldb->ctrl_reg, 0);557 558	return 0;559}560 561static const struct dev_pm_ops imx8qm_ldb_pm_ops = {562	SET_RUNTIME_PM_OPS(imx8qm_ldb_runtime_suspend,563			   imx8qm_ldb_runtime_resume, NULL)564};565 566static const struct of_device_id imx8qm_ldb_dt_ids[] = {567	{ .compatible = "fsl,imx8qm-ldb" },568	{ /* sentinel */ }569};570MODULE_DEVICE_TABLE(of, imx8qm_ldb_dt_ids);571 572static struct platform_driver imx8qm_ldb_driver = {573	.probe	= imx8qm_ldb_probe,574	.remove_new = imx8qm_ldb_remove,575	.driver	= {576		.pm = &imx8qm_ldb_pm_ops,577		.name = DRIVER_NAME,578		.of_match_table = imx8qm_ldb_dt_ids,579	},580};581module_platform_driver(imx8qm_ldb_driver);582 583MODULE_DESCRIPTION("i.MX8QM LVDS Display Bridge(LDB)/Pixel Mapper bridge driver");584MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");585MODULE_LICENSE("GPL v2");586MODULE_ALIAS("platform:" DRIVER_NAME);587