Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.9.
  1// SPDX-License-Identifier: GPL-2.0+
  2
  3/*
  4 * Copyright 2020 NXP
  5 */
  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			0x0
 23#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			0x20
 43#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			0x4
 50#define PC_REG_CLR			0x8
 51
 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 void
 82imx8qxp_pc_write(struct imx8qxp_pc *pc, unsigned int offset, u32 value)
 83{
 84	writel(value, pc->base + offset);
 85}
 86
 87static inline void
 88imx8qxp_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 void
 94imx8qxp_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_status
100imx8qxp_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 void
128imx8qxp_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 void
180imx8qxp_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 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 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	RUNTIME_PM_OPS(imx8qxp_pc_runtime_suspend, imx8qxp_pc_runtime_resume, NULL)
419};
420
421static const struct of_device_id imx8qxp_pc_dt_ids[] = {
422	{ .compatible = "fsl,imx8qm-pixel-combiner", },
423	{ .compatible = "fsl,imx8qxp-pixel-combiner", },
424	{ /* sentinel */ }
425};
426MODULE_DEVICE_TABLE(of, imx8qxp_pc_dt_ids);
427
428static struct platform_driver imx8qxp_pc_bridge_driver = {
429	.probe	= imx8qxp_pc_bridge_probe,
430	.remove = imx8qxp_pc_bridge_remove,
431	.driver	= {
432		.pm = pm_ptr(&imx8qxp_pc_pm_ops),
433		.name = DRIVER_NAME,
434		.of_match_table = imx8qxp_pc_dt_ids,
435	},
436};
437module_platform_driver(imx8qxp_pc_bridge_driver);
438
439MODULE_DESCRIPTION("i.MX8QM/QXP pixel combiner bridge driver");
440MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");
441MODULE_LICENSE("GPL v2");
442MODULE_ALIAS("platform:" DRIVER_NAME);