1 /*
2  * Copyright 2023-2024 NXP
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/drivers/dma.h>
9 #include <zephyr/drivers/dma/dma_mcux_pxp.h>
10 #include <zephyr/devicetree.h>
11 
12 #include <fsl_pxp.h>
13 #ifdef CONFIG_HAS_MCUX_CACHE
14 #include <fsl_cache.h>
15 #endif
16 
17 #define DT_DRV_COMPAT nxp_pxp
18 
19 #include <zephyr/logging/log.h>
20 LOG_MODULE_REGISTER(dma_mcux_pxp, CONFIG_DMA_LOG_LEVEL);
21 
22 struct dma_mcux_pxp_config {
23 	PXP_Type *base;
24 	void (*irq_config_func)(const struct device *dev);
25 };
26 
27 struct dma_mcux_pxp_data {
28 	void *user_data;
29 	dma_callback_t dma_callback;
30 	uint32_t ps_buf_addr;
31 	uint32_t ps_buf_size;
32 	uint32_t out_buf_addr;
33 	uint32_t out_buf_size;
34 };
35 
dma_mcux_pxp_irq_handler(const struct device * dev)36 static void dma_mcux_pxp_irq_handler(const struct device *dev)
37 {
38 	const struct dma_mcux_pxp_config *config = dev->config;
39 	struct dma_mcux_pxp_data *data = dev->data;
40 
41 	PXP_ClearStatusFlags(config->base, kPXP_CompleteFlag);
42 #ifdef CONFIG_HAS_MCUX_CACHE
43 	DCACHE_InvalidateByRange((uint32_t)data->out_buf_addr, data->out_buf_size);
44 #endif
45 	if (data->dma_callback) {
46 		data->dma_callback(dev, data->user_data, 0, 0);
47 	}
48 }
49 
50 /* Configure a channel */
dma_mcux_pxp_configure(const struct device * dev,uint32_t channel,struct dma_config * config)51 static int dma_mcux_pxp_configure(const struct device *dev, uint32_t channel,
52 				  struct dma_config *config)
53 {
54 	const struct dma_mcux_pxp_config *dev_config = dev->config;
55 	struct dma_mcux_pxp_data *dev_data = dev->data;
56 	pxp_ps_buffer_config_t ps_buffer_cfg;
57 	pxp_output_buffer_config_t output_buffer_cfg;
58 	uint8_t bytes_per_pixel;
59 	pxp_rotate_degree_t rotate;
60 	pxp_flip_mode_t flip;
61 
62 	ARG_UNUSED(channel);
63 	if (config->channel_direction != MEMORY_TO_MEMORY) {
64 		return -ENOTSUP;
65 	}
66 	/*
67 	 * Use the DMA slot value to get the pixel format and rotation
68 	 * settings
69 	 */
70 	switch ((config->dma_slot & DMA_MCUX_PXP_CMD_MASK) >> DMA_MCUX_PXP_CMD_SHIFT) {
71 	case DMA_MCUX_PXP_CMD_ROTATE_0:
72 		rotate = kPXP_Rotate0;
73 		break;
74 	case DMA_MCUX_PXP_CMD_ROTATE_90:
75 		rotate = kPXP_Rotate90;
76 		break;
77 	case DMA_MCUX_PXP_CMD_ROTATE_180:
78 		rotate = kPXP_Rotate180;
79 		break;
80 	case DMA_MCUX_PXP_CMD_ROTATE_270:
81 		rotate = kPXP_Rotate270;
82 		break;
83 	default:
84 		return -ENOTSUP;
85 	}
86 	switch ((config->dma_slot & DMA_MCUX_PXP_FMT_MASK) >> DMA_MCUX_PXP_FMT_SHIFT) {
87 	case DMA_MCUX_PXP_FMT_RGB565:
88 		ps_buffer_cfg.pixelFormat = kPXP_PsPixelFormatRGB565;
89 		output_buffer_cfg.pixelFormat = kPXP_OutputPixelFormatRGB565;
90 		bytes_per_pixel = 2;
91 		break;
92 	case DMA_MCUX_PXP_FMT_RGB888:
93 #if (!(defined(FSL_FEATURE_PXP_HAS_NO_EXTEND_PIXEL_FORMAT) && \
94 	FSL_FEATURE_PXP_HAS_NO_EXTEND_PIXEL_FORMAT)) && \
95 	(!(defined(FSL_FEATURE_PXP_V3) && FSL_FEATURE_PXP_V3))
96 		ps_buffer_cfg.pixelFormat = kPXP_PsPixelFormatARGB8888;
97 #else
98 		ps_buffer_cfg.pixelFormat = kPXP_PsPixelFormatRGB888;
99 #endif
100 		output_buffer_cfg.pixelFormat = kPXP_OutputPixelFormatRGB888;
101 		bytes_per_pixel = 3;
102 		break;
103 	case DMA_MCUX_PXP_FMT_ARGB8888:
104 		ps_buffer_cfg.pixelFormat = kPXP_PsPixelFormatARGB8888;
105 		output_buffer_cfg.pixelFormat = kPXP_OutputPixelFormatARGB8888;
106 		bytes_per_pixel = 4;
107 		break;
108 	default:
109 		return -ENOTSUP;
110 	}
111 	/*
112 	 * Use the DMA linked_channel value to get the flip settings.
113 	 */
114 	switch ((config->linked_channel & DMA_MCUX_PXP_FLIP_MASK) >> DMA_MCUX_PXP_FLIP_SHIFT) {
115 	case DMA_MCUX_PXP_FLIP_DISABLE:
116 		flip = kPXP_FlipDisable;
117 		break;
118 	case DMA_MCUX_PXP_FLIP_HORIZONTAL:
119 		flip = kPXP_FlipHorizontal;
120 		break;
121 	case DMA_MCUX_PXP_FLIP_VERTICAL:
122 		flip = kPXP_FlipVertical;
123 		break;
124 	case DMA_MCUX_PXP_FLIP_BOTH:
125 		flip = kPXP_FlipBoth;
126 		break;
127 	default:
128 		return -ENOTSUP;
129 	}
130 	DCACHE_CleanByRange((uint32_t)config->head_block->source_address,
131 			    config->head_block->block_size);
132 
133 	/*
134 	 * Some notes on how specific fields of the DMA config are used by
135 	 * the PXP:
136 	 * head block source address: PS buffer source address
137 	 * head block destination address: Output buffer address
138 	 * head block block size: size of destination and source buffer
139 	 * source data size: width of source buffer in bytes (pitch)
140 	 * source burst length: height of source buffer in pixels
141 	 * dest data size: width of destination buffer in bytes (pitch)
142 	 * dest burst length: height of destination buffer in pixels
143 	 */
144 	ps_buffer_cfg.swapByte = false;
145 	ps_buffer_cfg.bufferAddr = config->head_block->source_address;
146 	ps_buffer_cfg.bufferAddrU = 0U;
147 	ps_buffer_cfg.bufferAddrV = 0U;
148 	ps_buffer_cfg.pitchBytes = config->source_data_size;
149 	PXP_SetProcessSurfaceBufferConfig(dev_config->base, &ps_buffer_cfg);
150 
151 	output_buffer_cfg.interlacedMode = kPXP_OutputProgressive;
152 	output_buffer_cfg.buffer0Addr = config->head_block->dest_address;
153 	output_buffer_cfg.buffer1Addr = 0U;
154 	output_buffer_cfg.pitchBytes = config->dest_data_size;
155 	output_buffer_cfg.width = (config->dest_data_size / bytes_per_pixel);
156 	output_buffer_cfg.height = config->dest_burst_length;
157 	PXP_SetOutputBufferConfig(dev_config->base, &output_buffer_cfg);
158 	/* We only support a process surface that covers the full buffer */
159 	PXP_SetProcessSurfacePosition(dev_config->base, 0U, 0U, output_buffer_cfg.width,
160 				      output_buffer_cfg.height);
161 	/* Setup rotation */
162 	PXP_SetRotateConfig(dev_config->base, kPXP_RotateProcessSurface, rotate, flip);
163 
164 	dev_data->ps_buf_addr = config->head_block->source_address;
165 	dev_data->ps_buf_size = config->head_block->block_size;
166 	dev_data->out_buf_addr = config->head_block->dest_address;
167 	dev_data->out_buf_size = config->head_block->block_size;
168 	dev_data->dma_callback = config->dma_callback;
169 	dev_data->user_data = config->user_data;
170 	return 0;
171 }
172 
dma_mcux_pxp_start(const struct device * dev,uint32_t channel)173 static int dma_mcux_pxp_start(const struct device *dev, uint32_t channel)
174 {
175 	const struct dma_mcux_pxp_config *config = dev->config;
176 	struct dma_mcux_pxp_data *data = dev->data;
177 #ifdef CONFIG_HAS_MCUX_CACHE
178 	DCACHE_CleanByRange((uint32_t)data->ps_buf_addr, data->ps_buf_size);
179 #endif
180 
181 	ARG_UNUSED(channel);
182 	PXP_Start(config->base);
183 	return 0;
184 }
185 
186 static DEVICE_API(dma, dma_mcux_pxp_api) = {
187 	.config = dma_mcux_pxp_configure,
188 	.start = dma_mcux_pxp_start,
189 };
190 
dma_mcux_pxp_init(const struct device * dev)191 static int dma_mcux_pxp_init(const struct device *dev)
192 {
193 	const struct dma_mcux_pxp_config *config = dev->config;
194 
195 	PXP_Init(config->base);
196 	PXP_SetProcessSurfaceBackGroundColor(config->base, 0U);
197 	/* Disable alpha surface and CSC1 */
198 	PXP_SetAlphaSurfacePosition(config->base, 0xFFFFU, 0xFFFFU, 0U, 0U);
199 	PXP_EnableCsc1(config->base, false);
200 	PXP_EnableInterrupts(config->base, kPXP_CompleteInterruptEnable);
201 	config->irq_config_func(dev);
202 	return 0;
203 }
204 
205 #define DMA_INIT(n)                                                                                \
206 	static void dma_pxp_config_func##n(const struct device *dev)                               \
207 	{                                                                                          \
208 		IF_ENABLED(DT_INST_IRQ_HAS_IDX(n, 0),                                              \
209 			   (IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority),                 \
210 					dma_mcux_pxp_irq_handler, DEVICE_DT_INST_GET(n), 0);       \
211 			    irq_enable(DT_INST_IRQ(n, irq));))                                     \
212 	}                                                                                          \
213                                                                                                    \
214 	static const struct dma_mcux_pxp_config dma_config_##n = {                                 \
215 		.base = (PXP_Type *)DT_INST_REG_ADDR(n),                                           \
216 		.irq_config_func = dma_pxp_config_func##n,                                         \
217 	};                                                                                         \
218                                                                                                    \
219 	static struct dma_mcux_pxp_data dma_data_##n;                                              \
220                                                                                                    \
221 	DEVICE_DT_INST_DEFINE(n, &dma_mcux_pxp_init, NULL, &dma_data_##n, &dma_config_##n,         \
222 			      PRE_KERNEL_1, CONFIG_DMA_INIT_PRIORITY, &dma_mcux_pxp_api);
223 
224 DT_INST_FOREACH_STATUS_OKAY(DMA_INIT)
225