Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  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	if (!bridge->encoder) {
123		DRM_DEV_ERROR(pc->dev, "missing encoder\n");
124		return -ENODEV;
125	}
126
127	return drm_bridge_attach(bridge->encoder,
128				 ch->next_bridge, bridge,
129				 DRM_BRIDGE_ATTACH_NO_CONNECTOR);
130}
131
132static void
133imx8qxp_pc_bridge_mode_set(struct drm_bridge *bridge,
134			   const struct drm_display_mode *mode,
135			   const struct drm_display_mode *adjusted_mode)
136{
137	struct imx8qxp_pc_channel *ch = bridge->driver_private;
138	struct imx8qxp_pc *pc = ch->pc;
139	u32 val;
140	int ret;
141
142	ret = pm_runtime_get_sync(pc->dev);
143	if (ret < 0)
144		DRM_DEV_ERROR(pc->dev,
145			      "failed to get runtime PM sync: %d\n", ret);
146
147	ret = clk_prepare_enable(pc->clk_apb);
148	if (ret)
149		DRM_DEV_ERROR(pc->dev, "%s: failed to enable apb clock: %d\n",
150			      __func__,  ret);
151
152	/* HSYNC to pixel link is active low. */
153	imx8qxp_pc_write_clr(pc, PC_CTRL_REG,
154			     PC_DISP_HSYNC_POLARITY(ch->stream_id));
155
156	/* VSYNC to pixel link is active low. */
157	imx8qxp_pc_write_clr(pc, PC_CTRL_REG,
158			     PC_DISP_VSYNC_POLARITY(ch->stream_id));
159
160	/* Data enable to pixel link is active high. */
161	imx8qxp_pc_write_set(pc, PC_CTRL_REG,
162			     PC_DISP_DVALID_POLARITY(ch->stream_id));
163
164	/* Mask the first frame output which may be incomplete. */
165	imx8qxp_pc_write_set(pc, PC_CTRL_REG, PC_VSYNC_MASK_ENABLE);
166
167	/* Only support RGB currently. */
168	val = imx8qxp_pc_read(pc, PC_CTRL_REG);
169	if (ch->stream_id == 0) {
170		val &= ~PC_DISP0_PIX_DATA_FORMAT_MASK;
171		val |= PC_DISP0_PIX_DATA_FORMAT(RGB);
172	} else {
173		val &= ~PC_DISP1_PIX_DATA_FORMAT_MASK;
174		val |= PC_DISP1_PIX_DATA_FORMAT(RGB);
175	}
176	imx8qxp_pc_write(pc, PC_CTRL_REG, val);
177
178	/* Only support bypass mode currently. */
179	imx8qxp_pc_write_set(pc, PC_CTRL_REG, PC_DISP_BYPASS(ch->stream_id));
180
181	clk_disable_unprepare(pc->clk_apb);
182}
183
184static void
185imx8qxp_pc_bridge_atomic_disable(struct drm_bridge *bridge,
186				 struct drm_bridge_state *old_bridge_state)
187{
188	struct imx8qxp_pc_channel *ch = bridge->driver_private;
189	struct imx8qxp_pc *pc = ch->pc;
190	int ret;
191
192	ret = pm_runtime_put(pc->dev);
193	if (ret < 0)
194		DRM_DEV_ERROR(pc->dev, "failed to put runtime PM: %d\n", ret);
195}
196
197static const u32 imx8qxp_pc_bus_output_fmts[] = {
198	MEDIA_BUS_FMT_RGB888_1X36_CPADLO,
199	MEDIA_BUS_FMT_RGB666_1X36_CPADLO,
200};
201
202static bool imx8qxp_pc_bus_output_fmt_supported(u32 fmt)
203{
204	int i;
205
206	for (i = 0; i < ARRAY_SIZE(imx8qxp_pc_bus_output_fmts); i++) {
207		if (imx8qxp_pc_bus_output_fmts[i] == fmt)
208			return true;
209	}
210
211	return false;
212}
213
214static u32 *
215imx8qxp_pc_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
216					    struct drm_bridge_state *bridge_state,
217					    struct drm_crtc_state *crtc_state,
218					    struct drm_connector_state *conn_state,
219					    u32 output_fmt,
220					    unsigned int *num_input_fmts)
221{
222	u32 *input_fmts;
223
224	if (!imx8qxp_pc_bus_output_fmt_supported(output_fmt))
225		return NULL;
226
227	*num_input_fmts = 1;
228
229	input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);
230	if (!input_fmts)
231		return NULL;
232
233	switch (output_fmt) {
234	case MEDIA_BUS_FMT_RGB888_1X36_CPADLO:
235		input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X30_CPADLO;
236		break;
237	case MEDIA_BUS_FMT_RGB666_1X36_CPADLO:
238		input_fmts[0] = MEDIA_BUS_FMT_RGB666_1X30_CPADLO;
239		break;
240	default:
241		kfree(input_fmts);
242		input_fmts = NULL;
243		break;
244	}
245
246	return input_fmts;
247}
248
249static u32 *
250imx8qxp_pc_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
251					     struct drm_bridge_state *bridge_state,
252					     struct drm_crtc_state *crtc_state,
253					     struct drm_connector_state *conn_state,
254					     unsigned int *num_output_fmts)
255{
256	*num_output_fmts = ARRAY_SIZE(imx8qxp_pc_bus_output_fmts);
257	return kmemdup(imx8qxp_pc_bus_output_fmts,
258			sizeof(imx8qxp_pc_bus_output_fmts), GFP_KERNEL);
259}
260
261static const struct drm_bridge_funcs imx8qxp_pc_bridge_funcs = {
262	.atomic_duplicate_state	= drm_atomic_helper_bridge_duplicate_state,
263	.atomic_destroy_state	= drm_atomic_helper_bridge_destroy_state,
264	.atomic_reset		= drm_atomic_helper_bridge_reset,
265	.mode_valid		= imx8qxp_pc_bridge_mode_valid,
266	.attach			= imx8qxp_pc_bridge_attach,
267	.mode_set		= imx8qxp_pc_bridge_mode_set,
268	.atomic_disable		= imx8qxp_pc_bridge_atomic_disable,
269	.atomic_get_input_bus_fmts =
270				imx8qxp_pc_bridge_atomic_get_input_bus_fmts,
271	.atomic_get_output_bus_fmts =
272				imx8qxp_pc_bridge_atomic_get_output_bus_fmts,
273};
274
275static int imx8qxp_pc_bridge_probe(struct platform_device *pdev)
276{
277	struct imx8qxp_pc *pc;
278	struct imx8qxp_pc_channel *ch;
279	struct device *dev = &pdev->dev;
280	struct device_node *np = dev->of_node;
281	struct device_node *child, *remote;
282	u32 i;
283	int ret;
284
285	pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
286	if (!pc)
287		return -ENOMEM;
288
289	pc->base = devm_platform_ioremap_resource(pdev, 0);
290	if (IS_ERR(pc->base))
291		return PTR_ERR(pc->base);
292
293	pc->dev = dev;
294
295	pc->clk_apb = devm_clk_get(dev, "apb");
296	if (IS_ERR(pc->clk_apb)) {
297		ret = PTR_ERR(pc->clk_apb);
298		if (ret != -EPROBE_DEFER)
299			DRM_DEV_ERROR(dev, "failed to get apb clock: %d\n", ret);
300		return ret;
301	}
302
303	platform_set_drvdata(pdev, pc);
304	pm_runtime_enable(dev);
305
306	for_each_available_child_of_node(np, child) {
307		ret = of_property_read_u32(child, "reg", &i);
308		if (ret || i > 1) {
309			ret = -EINVAL;
310			DRM_DEV_ERROR(dev,
311				      "invalid channel(%u) node address\n", i);
312			goto free_child;
313		}
314
315		ch = &pc->ch[i];
316		ch->pc = pc;
317		ch->stream_id = i;
318
319		remote = of_graph_get_remote_node(child, 1, 0);
320		if (!remote) {
321			ret = -ENODEV;
322			DRM_DEV_ERROR(dev,
323				      "channel%u failed to get port1's remote node: %d\n",
324				      i, ret);
325			goto free_child;
326		}
327
328		ch->next_bridge = of_drm_find_bridge(remote);
329		if (!ch->next_bridge) {
330			of_node_put(remote);
331			ret = -EPROBE_DEFER;
332			DRM_DEV_DEBUG_DRIVER(dev,
333					     "channel%u failed to find next bridge: %d\n",
334					     i, ret);
335			goto free_child;
336		}
337
338		of_node_put(remote);
339
340		ch->bridge.driver_private = ch;
341		ch->bridge.funcs = &imx8qxp_pc_bridge_funcs;
342		ch->bridge.of_node = child;
343		ch->is_available = true;
344
345		drm_bridge_add(&ch->bridge);
346	}
347
348	return 0;
349
350free_child:
351	of_node_put(child);
352
353	if (i == 1 && pc->ch[0].next_bridge)
354		drm_bridge_remove(&pc->ch[0].bridge);
355
356	pm_runtime_disable(dev);
357	return ret;
358}
359
360static void imx8qxp_pc_bridge_remove(struct platform_device *pdev)
361{
362	struct imx8qxp_pc *pc = platform_get_drvdata(pdev);
363	struct imx8qxp_pc_channel *ch;
364	int i;
365
366	for (i = 0; i < 2; i++) {
367		ch = &pc->ch[i];
368
369		if (!ch->is_available)
370			continue;
371
372		drm_bridge_remove(&ch->bridge);
373		ch->is_available = false;
374	}
375
376	pm_runtime_disable(&pdev->dev);
377}
378
379static int __maybe_unused imx8qxp_pc_runtime_suspend(struct device *dev)
380{
381	struct platform_device *pdev = to_platform_device(dev);
382	struct imx8qxp_pc *pc = platform_get_drvdata(pdev);
383	int ret;
384
385	ret = clk_prepare_enable(pc->clk_apb);
386	if (ret)
387		DRM_DEV_ERROR(pc->dev, "%s: failed to enable apb clock: %d\n",
388			      __func__,  ret);
389
390	/* Disable pixel combiner by full reset. */
391	imx8qxp_pc_write_clr(pc, PC_SW_RESET_REG, PC_FULL_RESET_N);
392
393	clk_disable_unprepare(pc->clk_apb);
394
395	/* Ensure the reset takes effect. */
396	usleep_range(10, 20);
397
398	return ret;
399}
400
401static int __maybe_unused imx8qxp_pc_runtime_resume(struct device *dev)
402{
403	struct platform_device *pdev = to_platform_device(dev);
404	struct imx8qxp_pc *pc = platform_get_drvdata(pdev);
405	int ret;
406
407	ret = clk_prepare_enable(pc->clk_apb);
408	if (ret) {
409		DRM_DEV_ERROR(pc->dev, "%s: failed to enable apb clock: %d\n",
410			      __func__, ret);
411		return ret;
412	}
413
414	/* out of reset */
415	imx8qxp_pc_write_set(pc, PC_SW_RESET_REG, PC_FULL_RESET_N);
416
417	clk_disable_unprepare(pc->clk_apb);
418
419	return ret;
420}
421
422static const struct dev_pm_ops imx8qxp_pc_pm_ops = {
423	SET_RUNTIME_PM_OPS(imx8qxp_pc_runtime_suspend,
424			   imx8qxp_pc_runtime_resume, NULL)
425};
426
427static const struct of_device_id imx8qxp_pc_dt_ids[] = {
428	{ .compatible = "fsl,imx8qm-pixel-combiner", },
429	{ .compatible = "fsl,imx8qxp-pixel-combiner", },
430	{ /* sentinel */ }
431};
432MODULE_DEVICE_TABLE(of, imx8qxp_pc_dt_ids);
433
434static struct platform_driver imx8qxp_pc_bridge_driver = {
435	.probe	= imx8qxp_pc_bridge_probe,
436	.remove_new = imx8qxp_pc_bridge_remove,
437	.driver	= {
438		.pm = &imx8qxp_pc_pm_ops,
439		.name = DRIVER_NAME,
440		.of_match_table = imx8qxp_pc_dt_ids,
441	},
442};
443module_platform_driver(imx8qxp_pc_bridge_driver);
444
445MODULE_DESCRIPTION("i.MX8QM/QXP pixel combiner bridge driver");
446MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");
447MODULE_LICENSE("GPL v2");
448MODULE_ALIAS("platform:" DRIVER_NAME);