1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. |
4 | * http://www.samsung.com |
5 | * |
6 | * Samsung EXYNOS5 SoC series G-Scaler driver |
7 | */ |
8 | |
9 | #include <linux/io.h> |
10 | #include <linux/delay.h> |
11 | |
12 | #include "gsc-core.h" |
13 | |
14 | void gsc_hw_set_sw_reset(struct gsc_dev *dev) |
15 | { |
16 | writel(GSC_SW_RESET_SRESET, addr: dev->regs + GSC_SW_RESET); |
17 | } |
18 | |
19 | int gsc_wait_reset(struct gsc_dev *dev) |
20 | { |
21 | unsigned long end = jiffies + msecs_to_jiffies(m: 50); |
22 | u32 cfg; |
23 | |
24 | while (time_before(jiffies, end)) { |
25 | cfg = readl(addr: dev->regs + GSC_SW_RESET); |
26 | if (!cfg) |
27 | return 0; |
28 | usleep_range(min: 10, max: 20); |
29 | } |
30 | |
31 | return -EBUSY; |
32 | } |
33 | |
34 | void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask) |
35 | { |
36 | u32 cfg; |
37 | |
38 | cfg = readl(addr: dev->regs + GSC_IRQ); |
39 | if (mask) |
40 | cfg |= GSC_IRQ_FRMDONE_MASK; |
41 | else |
42 | cfg &= ~GSC_IRQ_FRMDONE_MASK; |
43 | writel(val: cfg, addr: dev->regs + GSC_IRQ); |
44 | } |
45 | |
46 | void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask) |
47 | { |
48 | u32 cfg; |
49 | |
50 | cfg = readl(addr: dev->regs + GSC_IRQ); |
51 | if (mask) |
52 | cfg |= GSC_IRQ_ENABLE; |
53 | else |
54 | cfg &= ~GSC_IRQ_ENABLE; |
55 | writel(val: cfg, addr: dev->regs + GSC_IRQ); |
56 | } |
57 | |
58 | void gsc_hw_set_input_buf_masking(struct gsc_dev *dev, u32 shift, |
59 | bool enable) |
60 | { |
61 | u32 cfg = readl(addr: dev->regs + GSC_IN_BASE_ADDR_Y_MASK); |
62 | u32 mask = 1 << shift; |
63 | |
64 | cfg &= ~mask; |
65 | cfg |= enable << shift; |
66 | |
67 | writel(val: cfg, addr: dev->regs + GSC_IN_BASE_ADDR_Y_MASK); |
68 | writel(val: cfg, addr: dev->regs + GSC_IN_BASE_ADDR_CB_MASK); |
69 | writel(val: cfg, addr: dev->regs + GSC_IN_BASE_ADDR_CR_MASK); |
70 | } |
71 | |
72 | void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift, |
73 | bool enable) |
74 | { |
75 | u32 cfg = readl(addr: dev->regs + GSC_OUT_BASE_ADDR_Y_MASK); |
76 | u32 mask = 1 << shift; |
77 | |
78 | cfg &= ~mask; |
79 | cfg |= enable << shift; |
80 | |
81 | writel(val: cfg, addr: dev->regs + GSC_OUT_BASE_ADDR_Y_MASK); |
82 | writel(val: cfg, addr: dev->regs + GSC_OUT_BASE_ADDR_CB_MASK); |
83 | writel(val: cfg, addr: dev->regs + GSC_OUT_BASE_ADDR_CR_MASK); |
84 | } |
85 | |
86 | void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr, |
87 | int index) |
88 | { |
89 | pr_debug("src_buf[%d]: %pad, cb: %pad, cr: %pad" , index, |
90 | &addr->y, &addr->cb, &addr->cr); |
91 | writel(val: addr->y, addr: dev->regs + GSC_IN_BASE_ADDR_Y(index)); |
92 | writel(val: addr->cb, addr: dev->regs + GSC_IN_BASE_ADDR_CB(index)); |
93 | writel(val: addr->cr, addr: dev->regs + GSC_IN_BASE_ADDR_CR(index)); |
94 | |
95 | } |
96 | |
97 | void gsc_hw_set_output_addr(struct gsc_dev *dev, |
98 | struct gsc_addr *addr, int index) |
99 | { |
100 | pr_debug("dst_buf[%d]: %pad, cb: %pad, cr: %pad" , |
101 | index, &addr->y, &addr->cb, &addr->cr); |
102 | writel(val: addr->y, addr: dev->regs + GSC_OUT_BASE_ADDR_Y(index)); |
103 | writel(val: addr->cb, addr: dev->regs + GSC_OUT_BASE_ADDR_CB(index)); |
104 | writel(val: addr->cr, addr: dev->regs + GSC_OUT_BASE_ADDR_CR(index)); |
105 | } |
106 | |
107 | void gsc_hw_set_input_path(struct gsc_ctx *ctx) |
108 | { |
109 | struct gsc_dev *dev = ctx->gsc_dev; |
110 | |
111 | u32 cfg = readl(addr: dev->regs + GSC_IN_CON); |
112 | cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK); |
113 | |
114 | if (ctx->in_path == GSC_DMA) |
115 | cfg |= GSC_IN_PATH_MEMORY; |
116 | |
117 | writel(val: cfg, addr: dev->regs + GSC_IN_CON); |
118 | } |
119 | |
120 | void gsc_hw_set_in_size(struct gsc_ctx *ctx) |
121 | { |
122 | struct gsc_dev *dev = ctx->gsc_dev; |
123 | struct gsc_frame *frame = &ctx->s_frame; |
124 | u32 cfg; |
125 | |
126 | /* Set input pixel offset */ |
127 | cfg = GSC_SRCIMG_OFFSET_X(frame->crop.left); |
128 | cfg |= GSC_SRCIMG_OFFSET_Y(frame->crop.top); |
129 | writel(val: cfg, addr: dev->regs + GSC_SRCIMG_OFFSET); |
130 | |
131 | /* Set input original size */ |
132 | cfg = GSC_SRCIMG_WIDTH(frame->f_width); |
133 | cfg |= GSC_SRCIMG_HEIGHT(frame->f_height); |
134 | writel(val: cfg, addr: dev->regs + GSC_SRCIMG_SIZE); |
135 | |
136 | /* Set input cropped size */ |
137 | cfg = GSC_CROPPED_WIDTH(frame->crop.width); |
138 | cfg |= GSC_CROPPED_HEIGHT(frame->crop.height); |
139 | writel(val: cfg, addr: dev->regs + GSC_CROPPED_SIZE); |
140 | } |
141 | |
142 | void gsc_hw_set_in_image_rgb(struct gsc_ctx *ctx) |
143 | { |
144 | struct gsc_dev *dev = ctx->gsc_dev; |
145 | struct gsc_frame *frame = &ctx->s_frame; |
146 | u32 cfg; |
147 | |
148 | cfg = readl(addr: dev->regs + GSC_IN_CON); |
149 | if (frame->colorspace == V4L2_COLORSPACE_REC709) |
150 | cfg |= GSC_IN_RGB_HD_WIDE; |
151 | else |
152 | cfg |= GSC_IN_RGB_SD_WIDE; |
153 | |
154 | if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB565X) |
155 | cfg |= GSC_IN_RGB565; |
156 | else if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB32) |
157 | cfg |= GSC_IN_XRGB8888; |
158 | |
159 | writel(val: cfg, addr: dev->regs + GSC_IN_CON); |
160 | } |
161 | |
162 | void gsc_hw_set_in_image_format(struct gsc_ctx *ctx) |
163 | { |
164 | struct gsc_dev *dev = ctx->gsc_dev; |
165 | struct gsc_frame *frame = &ctx->s_frame; |
166 | u32 i, depth = 0; |
167 | u32 cfg; |
168 | |
169 | cfg = readl(addr: dev->regs + GSC_IN_CON); |
170 | cfg &= ~(GSC_IN_RGB_TYPE_MASK | GSC_IN_YUV422_1P_ORDER_MASK | |
171 | GSC_IN_CHROMA_ORDER_MASK | GSC_IN_FORMAT_MASK | |
172 | GSC_IN_TILE_TYPE_MASK | GSC_IN_TILE_MODE); |
173 | writel(val: cfg, addr: dev->regs + GSC_IN_CON); |
174 | |
175 | if (is_rgb(frame->fmt->color)) { |
176 | gsc_hw_set_in_image_rgb(ctx); |
177 | return; |
178 | } |
179 | for (i = 0; i < frame->fmt->num_planes; i++) |
180 | depth += frame->fmt->depth[i]; |
181 | |
182 | switch (frame->fmt->num_comp) { |
183 | case 1: |
184 | cfg |= GSC_IN_YUV422_1P; |
185 | if (frame->fmt->yorder == GSC_LSB_Y) |
186 | cfg |= GSC_IN_YUV422_1P_ORDER_LSB_Y; |
187 | else |
188 | cfg |= GSC_IN_YUV422_1P_OEDER_LSB_C; |
189 | if (frame->fmt->corder == GSC_CBCR) |
190 | cfg |= GSC_IN_CHROMA_ORDER_CBCR; |
191 | else |
192 | cfg |= GSC_IN_CHROMA_ORDER_CRCB; |
193 | break; |
194 | case 2: |
195 | if (depth == 12) |
196 | cfg |= GSC_IN_YUV420_2P; |
197 | else |
198 | cfg |= GSC_IN_YUV422_2P; |
199 | if (frame->fmt->corder == GSC_CBCR) |
200 | cfg |= GSC_IN_CHROMA_ORDER_CBCR; |
201 | else |
202 | cfg |= GSC_IN_CHROMA_ORDER_CRCB; |
203 | break; |
204 | case 3: |
205 | if (depth == 12) |
206 | cfg |= GSC_IN_YUV420_3P; |
207 | else |
208 | cfg |= GSC_IN_YUV422_3P; |
209 | break; |
210 | } |
211 | |
212 | if (is_tiled(fmt: frame->fmt)) |
213 | cfg |= GSC_IN_TILE_C_16x8 | GSC_IN_TILE_MODE; |
214 | |
215 | writel(val: cfg, addr: dev->regs + GSC_IN_CON); |
216 | } |
217 | |
218 | void gsc_hw_set_output_path(struct gsc_ctx *ctx) |
219 | { |
220 | struct gsc_dev *dev = ctx->gsc_dev; |
221 | |
222 | u32 cfg = readl(addr: dev->regs + GSC_OUT_CON); |
223 | cfg &= ~GSC_OUT_PATH_MASK; |
224 | |
225 | if (ctx->out_path == GSC_DMA) |
226 | cfg |= GSC_OUT_PATH_MEMORY; |
227 | else |
228 | cfg |= GSC_OUT_PATH_LOCAL; |
229 | |
230 | writel(val: cfg, addr: dev->regs + GSC_OUT_CON); |
231 | } |
232 | |
233 | void gsc_hw_set_out_size(struct gsc_ctx *ctx) |
234 | { |
235 | struct gsc_dev *dev = ctx->gsc_dev; |
236 | struct gsc_frame *frame = &ctx->d_frame; |
237 | u32 cfg; |
238 | |
239 | /* Set output original size */ |
240 | if (ctx->out_path == GSC_DMA) { |
241 | cfg = GSC_DSTIMG_OFFSET_X(frame->crop.left); |
242 | cfg |= GSC_DSTIMG_OFFSET_Y(frame->crop.top); |
243 | writel(val: cfg, addr: dev->regs + GSC_DSTIMG_OFFSET); |
244 | |
245 | cfg = GSC_DSTIMG_WIDTH(frame->f_width); |
246 | cfg |= GSC_DSTIMG_HEIGHT(frame->f_height); |
247 | writel(val: cfg, addr: dev->regs + GSC_DSTIMG_SIZE); |
248 | } |
249 | |
250 | /* Set output scaled size */ |
251 | if (ctx->gsc_ctrls.rotate->val == 90 || |
252 | ctx->gsc_ctrls.rotate->val == 270) { |
253 | cfg = GSC_SCALED_WIDTH(frame->crop.height); |
254 | cfg |= GSC_SCALED_HEIGHT(frame->crop.width); |
255 | } else { |
256 | cfg = GSC_SCALED_WIDTH(frame->crop.width); |
257 | cfg |= GSC_SCALED_HEIGHT(frame->crop.height); |
258 | } |
259 | writel(val: cfg, addr: dev->regs + GSC_SCALED_SIZE); |
260 | } |
261 | |
262 | void gsc_hw_set_out_image_rgb(struct gsc_ctx *ctx) |
263 | { |
264 | struct gsc_dev *dev = ctx->gsc_dev; |
265 | struct gsc_frame *frame = &ctx->d_frame; |
266 | u32 cfg; |
267 | |
268 | cfg = readl(addr: dev->regs + GSC_OUT_CON); |
269 | if (frame->colorspace == V4L2_COLORSPACE_REC709) |
270 | cfg |= GSC_OUT_RGB_HD_WIDE; |
271 | else |
272 | cfg |= GSC_OUT_RGB_SD_WIDE; |
273 | |
274 | if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB565X) |
275 | cfg |= GSC_OUT_RGB565; |
276 | else if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB32) |
277 | cfg |= GSC_OUT_XRGB8888; |
278 | |
279 | writel(val: cfg, addr: dev->regs + GSC_OUT_CON); |
280 | } |
281 | |
282 | void gsc_hw_set_out_image_format(struct gsc_ctx *ctx) |
283 | { |
284 | struct gsc_dev *dev = ctx->gsc_dev; |
285 | struct gsc_frame *frame = &ctx->d_frame; |
286 | u32 i, depth = 0; |
287 | u32 cfg; |
288 | |
289 | cfg = readl(addr: dev->regs + GSC_OUT_CON); |
290 | cfg &= ~(GSC_OUT_RGB_TYPE_MASK | GSC_OUT_YUV422_1P_ORDER_MASK | |
291 | GSC_OUT_CHROMA_ORDER_MASK | GSC_OUT_FORMAT_MASK | |
292 | GSC_OUT_TILE_TYPE_MASK | GSC_OUT_TILE_MODE); |
293 | writel(val: cfg, addr: dev->regs + GSC_OUT_CON); |
294 | |
295 | if (is_rgb(frame->fmt->color)) { |
296 | gsc_hw_set_out_image_rgb(ctx); |
297 | return; |
298 | } |
299 | |
300 | if (ctx->out_path != GSC_DMA) { |
301 | cfg |= GSC_OUT_YUV444; |
302 | goto end_set; |
303 | } |
304 | |
305 | for (i = 0; i < frame->fmt->num_planes; i++) |
306 | depth += frame->fmt->depth[i]; |
307 | |
308 | switch (frame->fmt->num_comp) { |
309 | case 1: |
310 | cfg |= GSC_OUT_YUV422_1P; |
311 | if (frame->fmt->yorder == GSC_LSB_Y) |
312 | cfg |= GSC_OUT_YUV422_1P_ORDER_LSB_Y; |
313 | else |
314 | cfg |= GSC_OUT_YUV422_1P_OEDER_LSB_C; |
315 | if (frame->fmt->corder == GSC_CBCR) |
316 | cfg |= GSC_OUT_CHROMA_ORDER_CBCR; |
317 | else |
318 | cfg |= GSC_OUT_CHROMA_ORDER_CRCB; |
319 | break; |
320 | case 2: |
321 | if (depth == 12) |
322 | cfg |= GSC_OUT_YUV420_2P; |
323 | else |
324 | cfg |= GSC_OUT_YUV422_2P; |
325 | if (frame->fmt->corder == GSC_CBCR) |
326 | cfg |= GSC_OUT_CHROMA_ORDER_CBCR; |
327 | else |
328 | cfg |= GSC_OUT_CHROMA_ORDER_CRCB; |
329 | break; |
330 | case 3: |
331 | cfg |= GSC_OUT_YUV420_3P; |
332 | break; |
333 | } |
334 | |
335 | if (is_tiled(fmt: frame->fmt)) |
336 | cfg |= GSC_OUT_TILE_C_16x8 | GSC_OUT_TILE_MODE; |
337 | |
338 | end_set: |
339 | writel(val: cfg, addr: dev->regs + GSC_OUT_CON); |
340 | } |
341 | |
342 | void gsc_hw_set_prescaler(struct gsc_ctx *ctx) |
343 | { |
344 | struct gsc_dev *dev = ctx->gsc_dev; |
345 | struct gsc_scaler *sc = &ctx->scaler; |
346 | u32 cfg; |
347 | |
348 | cfg = GSC_PRESC_SHFACTOR(sc->pre_shfactor); |
349 | cfg |= GSC_PRESC_H_RATIO(sc->pre_hratio); |
350 | cfg |= GSC_PRESC_V_RATIO(sc->pre_vratio); |
351 | writel(val: cfg, addr: dev->regs + GSC_PRE_SCALE_RATIO); |
352 | } |
353 | |
354 | void gsc_hw_set_mainscaler(struct gsc_ctx *ctx) |
355 | { |
356 | struct gsc_dev *dev = ctx->gsc_dev; |
357 | struct gsc_scaler *sc = &ctx->scaler; |
358 | u32 cfg; |
359 | |
360 | cfg = GSC_MAIN_H_RATIO_VALUE(sc->main_hratio); |
361 | writel(val: cfg, addr: dev->regs + GSC_MAIN_H_RATIO); |
362 | |
363 | cfg = GSC_MAIN_V_RATIO_VALUE(sc->main_vratio); |
364 | writel(val: cfg, addr: dev->regs + GSC_MAIN_V_RATIO); |
365 | } |
366 | |
367 | void gsc_hw_set_rotation(struct gsc_ctx *ctx) |
368 | { |
369 | struct gsc_dev *dev = ctx->gsc_dev; |
370 | u32 cfg; |
371 | |
372 | cfg = readl(addr: dev->regs + GSC_IN_CON); |
373 | cfg &= ~GSC_IN_ROT_MASK; |
374 | |
375 | switch (ctx->gsc_ctrls.rotate->val) { |
376 | case 270: |
377 | cfg |= GSC_IN_ROT_270; |
378 | break; |
379 | case 180: |
380 | cfg |= GSC_IN_ROT_180; |
381 | break; |
382 | case 90: |
383 | if (ctx->gsc_ctrls.hflip->val) |
384 | cfg |= GSC_IN_ROT_90_XFLIP; |
385 | else if (ctx->gsc_ctrls.vflip->val) |
386 | cfg |= GSC_IN_ROT_90_YFLIP; |
387 | else |
388 | cfg |= GSC_IN_ROT_90; |
389 | break; |
390 | case 0: |
391 | if (ctx->gsc_ctrls.hflip->val) |
392 | cfg |= GSC_IN_ROT_XFLIP; |
393 | else if (ctx->gsc_ctrls.vflip->val) |
394 | cfg |= GSC_IN_ROT_YFLIP; |
395 | } |
396 | |
397 | writel(val: cfg, addr: dev->regs + GSC_IN_CON); |
398 | } |
399 | |
400 | void gsc_hw_set_global_alpha(struct gsc_ctx *ctx) |
401 | { |
402 | struct gsc_dev *dev = ctx->gsc_dev; |
403 | struct gsc_frame *frame = &ctx->d_frame; |
404 | u32 cfg; |
405 | |
406 | if (!is_rgb(frame->fmt->color)) { |
407 | pr_debug("Not a RGB format" ); |
408 | return; |
409 | } |
410 | |
411 | cfg = readl(addr: dev->regs + GSC_OUT_CON); |
412 | cfg &= ~GSC_OUT_GLOBAL_ALPHA_MASK; |
413 | |
414 | cfg |= GSC_OUT_GLOBAL_ALPHA(ctx->gsc_ctrls.global_alpha->val); |
415 | writel(val: cfg, addr: dev->regs + GSC_OUT_CON); |
416 | } |
417 | |
418 | void gsc_hw_set_sfr_update(struct gsc_ctx *ctx) |
419 | { |
420 | struct gsc_dev *dev = ctx->gsc_dev; |
421 | u32 cfg; |
422 | |
423 | cfg = readl(addr: dev->regs + GSC_ENABLE); |
424 | cfg |= GSC_ENABLE_SFR_UPDATE; |
425 | writel(val: cfg, addr: dev->regs + GSC_ENABLE); |
426 | } |
427 | |