blob: 4447cbca4e234a678767e6d29a341055af765df5 [file] [log] [blame]
Benjamin Chan99eb63b2016-12-21 15:45:26 -05001/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
Alan Kwong9487de22016-01-16 22:06:36 -05002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#define pr_fmt(fmt) "%s: " fmt, __func__
15
16#include <linux/platform_device.h>
17#include <linux/module.h>
18#include <linux/fs.h>
19#include <linux/file.h>
20#include <linux/sync.h>
21#include <linux/delay.h>
22#include <linux/debugfs.h>
23#include <linux/interrupt.h>
24#include <linux/dma-mapping.h>
25#include <linux/dma-buf.h>
26#include <linux/msm_ion.h>
Alan Kwong315cd772016-08-03 22:29:42 -040027#include <linux/clk/msm-clk.h>
Alan Kwong9487de22016-01-16 22:06:36 -050028
29#include "sde_rotator_core.h"
30#include "sde_rotator_util.h"
31#include "sde_rotator_smmu.h"
32#include "sde_rotator_r3.h"
33#include "sde_rotator_r3_internal.h"
34#include "sde_rotator_r3_hwio.h"
35#include "sde_rotator_r3_debug.h"
36#include "sde_rotator_trace.h"
Benjamin Chan53e3bce2016-08-31 14:43:29 -040037#include "sde_rotator_debug.h"
Alan Kwong9487de22016-01-16 22:06:36 -050038
Benjamin Chan99eb63b2016-12-21 15:45:26 -050039#define RES_UHD (3840*2160)
40
41/* traffic shaping clock ticks = finish_time x 19.2MHz */
42#define TRAFFIC_SHAPE_CLKTICK_14MS 268800
43#define TRAFFIC_SHAPE_CLKTICK_12MS 230400
44
Alan Kwong9487de22016-01-16 22:06:36 -050045/* XIN mapping */
46#define XIN_SSPP 0
47#define XIN_WRITEBACK 1
48
49/* wait for at most 2 vsync for lowest refresh rate (24hz) */
50#define KOFF_TIMEOUT msecs_to_jiffies(42 * 32)
51
52/* Macro for constructing the REGDMA command */
53#define SDE_REGDMA_WRITE(p, off, data) \
54 do { \
55 *p++ = REGDMA_OP_REGWRITE | \
56 ((off) & REGDMA_ADDR_OFFSET_MASK); \
57 *p++ = (data); \
58 } while (0)
59
60#define SDE_REGDMA_MODIFY(p, off, mask, data) \
61 do { \
62 *p++ = REGDMA_OP_REGMODIFY | \
63 ((off) & REGDMA_ADDR_OFFSET_MASK); \
64 *p++ = (mask); \
65 *p++ = (data); \
66 } while (0)
67
68#define SDE_REGDMA_BLKWRITE_INC(p, off, len) \
69 do { \
70 *p++ = REGDMA_OP_BLKWRITE_INC | \
71 ((off) & REGDMA_ADDR_OFFSET_MASK); \
72 *p++ = (len); \
73 } while (0)
74
75#define SDE_REGDMA_BLKWRITE_DATA(p, data) \
76 do { \
77 *(p) = (data); \
78 (p)++; \
79 } while (0)
80
81/* Macro for directly accessing mapped registers */
82#define SDE_ROTREG_WRITE(base, off, data) \
83 writel_relaxed(data, (base + (off)))
84
85#define SDE_ROTREG_READ(base, off) \
86 readl_relaxed(base + (off))
87
Alan Kwongda16e442016-08-14 20:47:18 -040088static u32 sde_hw_rotator_input_pixfmts[] = {
89 SDE_PIX_FMT_XRGB_8888,
90 SDE_PIX_FMT_ARGB_8888,
91 SDE_PIX_FMT_ABGR_8888,
92 SDE_PIX_FMT_RGBA_8888,
93 SDE_PIX_FMT_BGRA_8888,
94 SDE_PIX_FMT_RGBX_8888,
95 SDE_PIX_FMT_BGRX_8888,
96 SDE_PIX_FMT_XBGR_8888,
97 SDE_PIX_FMT_RGBA_5551,
98 SDE_PIX_FMT_ARGB_1555,
99 SDE_PIX_FMT_ABGR_1555,
100 SDE_PIX_FMT_BGRA_5551,
101 SDE_PIX_FMT_BGRX_5551,
102 SDE_PIX_FMT_RGBX_5551,
103 SDE_PIX_FMT_XBGR_1555,
104 SDE_PIX_FMT_XRGB_1555,
105 SDE_PIX_FMT_ARGB_4444,
106 SDE_PIX_FMT_RGBA_4444,
107 SDE_PIX_FMT_BGRA_4444,
108 SDE_PIX_FMT_ABGR_4444,
109 SDE_PIX_FMT_RGBX_4444,
110 SDE_PIX_FMT_XRGB_4444,
111 SDE_PIX_FMT_BGRX_4444,
112 SDE_PIX_FMT_XBGR_4444,
113 SDE_PIX_FMT_RGB_888,
114 SDE_PIX_FMT_BGR_888,
115 SDE_PIX_FMT_RGB_565,
116 SDE_PIX_FMT_BGR_565,
117 SDE_PIX_FMT_Y_CB_CR_H2V2,
118 SDE_PIX_FMT_Y_CR_CB_H2V2,
119 SDE_PIX_FMT_Y_CR_CB_GH2V2,
120 SDE_PIX_FMT_Y_CBCR_H2V2,
121 SDE_PIX_FMT_Y_CRCB_H2V2,
122 SDE_PIX_FMT_Y_CBCR_H1V2,
123 SDE_PIX_FMT_Y_CRCB_H1V2,
124 SDE_PIX_FMT_Y_CBCR_H2V1,
125 SDE_PIX_FMT_Y_CRCB_H2V1,
126 SDE_PIX_FMT_YCBYCR_H2V1,
127 SDE_PIX_FMT_Y_CBCR_H2V2_VENUS,
128 SDE_PIX_FMT_Y_CRCB_H2V2_VENUS,
129 SDE_PIX_FMT_RGBA_8888_UBWC,
130 SDE_PIX_FMT_RGBX_8888_UBWC,
131 SDE_PIX_FMT_RGB_565_UBWC,
132 SDE_PIX_FMT_Y_CBCR_H2V2_UBWC,
133 SDE_PIX_FMT_RGBA_1010102,
134 SDE_PIX_FMT_RGBX_1010102,
135 SDE_PIX_FMT_ARGB_2101010,
136 SDE_PIX_FMT_XRGB_2101010,
137 SDE_PIX_FMT_BGRA_1010102,
138 SDE_PIX_FMT_BGRX_1010102,
139 SDE_PIX_FMT_ABGR_2101010,
140 SDE_PIX_FMT_XBGR_2101010,
141 SDE_PIX_FMT_RGBA_1010102_UBWC,
142 SDE_PIX_FMT_RGBX_1010102_UBWC,
143 SDE_PIX_FMT_Y_CBCR_H2V2_P010,
144 SDE_PIX_FMT_Y_CBCR_H2V2_TP10,
145 SDE_PIX_FMT_Y_CBCR_H2V2_TP10_UBWC,
146};
147
148static u32 sde_hw_rotator_output_pixfmts[] = {
149 SDE_PIX_FMT_XRGB_8888,
150 SDE_PIX_FMT_ARGB_8888,
151 SDE_PIX_FMT_ABGR_8888,
152 SDE_PIX_FMT_RGBA_8888,
153 SDE_PIX_FMT_BGRA_8888,
154 SDE_PIX_FMT_RGBX_8888,
155 SDE_PIX_FMT_BGRX_8888,
156 SDE_PIX_FMT_XBGR_8888,
157 SDE_PIX_FMT_RGBA_5551,
158 SDE_PIX_FMT_ARGB_1555,
159 SDE_PIX_FMT_ABGR_1555,
160 SDE_PIX_FMT_BGRA_5551,
161 SDE_PIX_FMT_BGRX_5551,
162 SDE_PIX_FMT_RGBX_5551,
163 SDE_PIX_FMT_XBGR_1555,
164 SDE_PIX_FMT_XRGB_1555,
165 SDE_PIX_FMT_ARGB_4444,
166 SDE_PIX_FMT_RGBA_4444,
167 SDE_PIX_FMT_BGRA_4444,
168 SDE_PIX_FMT_ABGR_4444,
169 SDE_PIX_FMT_RGBX_4444,
170 SDE_PIX_FMT_XRGB_4444,
171 SDE_PIX_FMT_BGRX_4444,
172 SDE_PIX_FMT_XBGR_4444,
173 SDE_PIX_FMT_RGB_888,
174 SDE_PIX_FMT_BGR_888,
175 SDE_PIX_FMT_RGB_565,
176 SDE_PIX_FMT_BGR_565,
177 /* SDE_PIX_FMT_Y_CB_CR_H2V2 */
178 /* SDE_PIX_FMT_Y_CR_CB_H2V2 */
179 /* SDE_PIX_FMT_Y_CR_CB_GH2V2 */
180 SDE_PIX_FMT_Y_CBCR_H2V2,
181 SDE_PIX_FMT_Y_CRCB_H2V2,
182 SDE_PIX_FMT_Y_CBCR_H1V2,
183 SDE_PIX_FMT_Y_CRCB_H1V2,
184 SDE_PIX_FMT_Y_CBCR_H2V1,
185 SDE_PIX_FMT_Y_CRCB_H2V1,
186 /* SDE_PIX_FMT_YCBYCR_H2V1 */
187 SDE_PIX_FMT_Y_CBCR_H2V2_VENUS,
188 SDE_PIX_FMT_Y_CRCB_H2V2_VENUS,
189 SDE_PIX_FMT_RGBA_8888_UBWC,
190 SDE_PIX_FMT_RGBX_8888_UBWC,
191 SDE_PIX_FMT_RGB_565_UBWC,
192 SDE_PIX_FMT_Y_CBCR_H2V2_UBWC,
193 SDE_PIX_FMT_RGBA_1010102,
194 SDE_PIX_FMT_RGBX_1010102,
195 /* SDE_PIX_FMT_ARGB_2101010 */
196 /* SDE_PIX_FMT_XRGB_2101010 */
197 SDE_PIX_FMT_BGRA_1010102,
198 SDE_PIX_FMT_BGRX_1010102,
199 /* SDE_PIX_FMT_ABGR_2101010 */
200 /* SDE_PIX_FMT_XBGR_2101010 */
201 SDE_PIX_FMT_RGBA_1010102_UBWC,
202 SDE_PIX_FMT_RGBX_1010102_UBWC,
203 SDE_PIX_FMT_Y_CBCR_H2V2_P010,
204 SDE_PIX_FMT_Y_CBCR_H2V2_TP10,
205 SDE_PIX_FMT_Y_CBCR_H2V2_TP10_UBWC,
206};
207
Benjamin Chan53e3bce2016-08-31 14:43:29 -0400208static struct sde_rot_vbif_debug_bus nrt_vbif_dbg_bus_r3[] = {
209 {0x214, 0x21c, 16, 1, 0x10}, /* arb clients */
210 {0x214, 0x21c, 0, 12, 0x13}, /* xin blocks - axi side */
211 {0x21c, 0x214, 0, 12, 0xc}, /* xin blocks - clock side */
212};
213
214static struct sde_rot_regdump sde_rot_r3_regdump[] = {
215 { "SDEROT_ROTTOP", SDE_ROT_ROTTOP_OFFSET, 0x100, SDE_ROT_REGDUMP_READ },
216 { "SDEROT_SSPP", SDE_ROT_SSPP_OFFSET, 0x200, SDE_ROT_REGDUMP_READ },
217 { "SDEROT_WB", SDE_ROT_WB_OFFSET, 0x300, SDE_ROT_REGDUMP_READ },
218 { "SDEROT_REGDMA_CSR", SDE_ROT_REGDMA_OFFSET, 0x100,
219 SDE_ROT_REGDUMP_READ },
220 /*
221 * Need to perform a SW reset to REGDMA in order to access the
222 * REGDMA RAM especially if REGDMA is waiting for Rotator IDLE.
223 * REGDMA RAM should be dump at last.
224 */
225 { "SDEROT_REGDMA_RESET", ROTTOP_SW_RESET_OVERRIDE, 1,
226 SDE_ROT_REGDUMP_WRITE },
227 { "SDEROT_REGDMA_RAM", SDE_ROT_REGDMA_RAM_OFFSET, 0x2000,
228 SDE_ROT_REGDUMP_READ },
Benjamin Chan59a06052017-01-12 18:06:03 -0500229 { "SDEROT_VBIF_NRT", SDE_ROT_VBIF_NRT_OFFSET, 0x590,
230 SDE_ROT_REGDUMP_VBIF },
Benjamin Chan53e3bce2016-08-31 14:43:29 -0400231};
232
Alan Kwong818b7fc2016-07-24 22:07:41 -0400233/* Invalid software timestamp value for initialization */
234#define SDE_REGDMA_SWTS_INVALID (~0)
235
236/**
237 * sde_hw_rotator_elapsed_swts - Find difference of 2 software timestamps
238 * @ts_curr: current software timestamp
239 * @ts_prev: previous software timestamp
240 * @return: the amount ts_curr is ahead of ts_prev
241 */
242static int sde_hw_rotator_elapsed_swts(u32 ts_curr, u32 ts_prev)
243{
244 u32 diff = (ts_curr - ts_prev) & SDE_REGDMA_SWTS_MASK;
245
246 return sign_extend32(diff, (SDE_REGDMA_SWTS_SHIFT - 1));
247}
248
249/**
250 * sde_hw_rotator_pending_swts - Check if the given context is still pending
251 * @rot: Pointer to hw rotator
252 * @ctx: Pointer to rotator context
253 * @pswts: Pointer to returned reference software timestamp, optional
254 * @return: true if context has pending requests
255 */
256static int sde_hw_rotator_pending_swts(struct sde_hw_rotator *rot,
257 struct sde_hw_rotator_context *ctx, u32 *pswts)
258{
259 u32 swts;
260 int ts_diff;
261 bool pending;
262
263 if (ctx->last_regdma_timestamp == SDE_REGDMA_SWTS_INVALID)
264 swts = SDE_ROTREG_READ(rot->mdss_base, REGDMA_TIMESTAMP_REG);
265 else
266 swts = ctx->last_regdma_timestamp;
267
268 if (ctx->q_id == ROT_QUEUE_LOW_PRIORITY)
269 swts >>= SDE_REGDMA_SWTS_SHIFT;
270
271 swts &= SDE_REGDMA_SWTS_MASK;
272
273 ts_diff = sde_hw_rotator_elapsed_swts(ctx->timestamp, swts);
274
275 if (pswts)
276 *pswts = swts;
277
278 pending = (ts_diff > 0) ? true : false;
279
280 SDEROT_DBG("ts:0x%x, queue_id:%d, swts:0x%x, pending:%d\n",
281 ctx->timestamp, ctx->q_id, swts, pending);
Benjamin Chan0f9e61d2016-09-16 16:01:09 -0400282 SDEROT_EVTLOG(ctx->timestamp, swts, ctx->q_id, ts_diff);
Alan Kwong818b7fc2016-07-24 22:07:41 -0400283 return pending;
284}
285
286/**
287 * sde_hw_rotator_enable_irq - Enable hw rotator interrupt with ref. count
288 * Also, clear rotator/regdma irq status.
289 * @rot: Pointer to hw rotator
290 */
291static void sde_hw_rotator_enable_irq(struct sde_hw_rotator *rot)
292{
293 SDEROT_DBG("irq_num:%d enabled:%d\n", rot->irq_num,
294 atomic_read(&rot->irq_enabled));
295
296 if (!atomic_read(&rot->irq_enabled)) {
297 if (rot->mode == ROT_REGDMA_OFF)
298 SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_CLEAR,
299 ROT_DONE_MASK);
300 else
301 SDE_ROTREG_WRITE(rot->mdss_base,
302 REGDMA_CSR_REGDMA_INT_CLEAR, REGDMA_INT_MASK);
303
304 enable_irq(rot->irq_num);
305 }
306 atomic_inc(&rot->irq_enabled);
307}
308
309/**
310 * sde_hw_rotator_disable_irq - Disable hw rotator interrupt with ref. count
311 * Also, clear rotator/regdma irq enable masks.
312 * @rot: Pointer to hw rotator
313 */
314static void sde_hw_rotator_disable_irq(struct sde_hw_rotator *rot)
315{
316 SDEROT_DBG("irq_num:%d enabled:%d\n", rot->irq_num,
317 atomic_read(&rot->irq_enabled));
318
319 if (!atomic_read(&rot->irq_enabled)) {
320 SDEROT_ERR("irq %d is already disabled\n", rot->irq_num);
321 return;
322 }
323
324 if (!atomic_dec_return(&rot->irq_enabled)) {
325 if (rot->mode == ROT_REGDMA_OFF)
326 SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_EN, 0);
327 else
328 SDE_ROTREG_WRITE(rot->mdss_base,
329 REGDMA_CSR_REGDMA_INT_EN, 0);
330 /* disable irq after last pending irq is handled, if any */
331 synchronize_irq(rot->irq_num);
332 disable_irq_nosync(rot->irq_num);
333 }
334}
335
336/**
337 * sde_hw_rotator_dump_status - Dump hw rotator status on error
338 * @rot: Pointer to hw rotator
339 */
340static void sde_hw_rotator_dump_status(struct sde_hw_rotator *rot)
341{
342 SDEROT_ERR(
343 "op_mode = %x, int_en = %x, int_status = %x\n",
344 SDE_ROTREG_READ(rot->mdss_base,
345 REGDMA_CSR_REGDMA_OP_MODE),
346 SDE_ROTREG_READ(rot->mdss_base,
347 REGDMA_CSR_REGDMA_INT_EN),
348 SDE_ROTREG_READ(rot->mdss_base,
349 REGDMA_CSR_REGDMA_INT_STATUS));
350
351 SDEROT_ERR(
352 "ts = %x, q0_status = %x, q1_status = %x, block_status = %x\n",
353 SDE_ROTREG_READ(rot->mdss_base,
354 REGDMA_TIMESTAMP_REG),
355 SDE_ROTREG_READ(rot->mdss_base,
356 REGDMA_CSR_REGDMA_QUEUE_0_STATUS),
357 SDE_ROTREG_READ(rot->mdss_base,
358 REGDMA_CSR_REGDMA_QUEUE_1_STATUS),
359 SDE_ROTREG_READ(rot->mdss_base,
360 REGDMA_CSR_REGDMA_BLOCK_STATUS));
361
362 SDEROT_ERR(
363 "invalid_cmd_offset = %x, fsm_state = %x\n",
364 SDE_ROTREG_READ(rot->mdss_base,
365 REGDMA_CSR_REGDMA_INVALID_CMD_RAM_OFFSET),
366 SDE_ROTREG_READ(rot->mdss_base,
367 REGDMA_CSR_REGDMA_FSM_STATE));
Benjamin Chan59a06052017-01-12 18:06:03 -0500368
369 SDEROT_ERR(
370 "UBWC decode status = %x, UBWC encode status = %x\n",
371 SDE_ROTREG_READ(rot->mdss_base, ROT_SSPP_UBWC_ERROR_STATUS),
372 SDE_ROTREG_READ(rot->mdss_base, ROT_WB_UBWC_ERROR_STATUS));
Alan Kwong818b7fc2016-07-24 22:07:41 -0400373}
374
Alan Kwong9487de22016-01-16 22:06:36 -0500375/**
376 * sde_hw_rotator_get_ctx(): Retrieve rotator context from rotator HW based
377 * on provided session_id. Each rotator has a different session_id.
378 */
379static struct sde_hw_rotator_context *sde_hw_rotator_get_ctx(
380 struct sde_hw_rotator *rot, u32 session_id,
381 enum sde_rot_queue_prio q_id)
382{
383 int i;
384 struct sde_hw_rotator_context *ctx = NULL;
385
386 for (i = 0; i < SDE_HW_ROT_REGDMA_TOTAL_CTX; i++) {
387 ctx = rot->rotCtx[q_id][i];
388
389 if (ctx && (ctx->session_id == session_id)) {
390 SDEROT_DBG(
391 "rotCtx sloti[%d][%d] ==> ctx:%p | session-id:%d\n",
392 q_id, i, ctx, ctx->session_id);
393 return ctx;
394 }
395 }
396
397 return NULL;
398}
399
400/*
401 * sde_hw_rotator_map_vaddr - map the debug buffer to kernel space
402 * @dbgbuf: Pointer to debug buffer
403 * @buf: Pointer to layer buffer structure
404 * @data: Pointer to h/w mapped buffer structure
405 */
406static void sde_hw_rotator_map_vaddr(struct sde_dbg_buf *dbgbuf,
407 struct sde_layer_buffer *buf, struct sde_mdp_data *data)
408{
409 dbgbuf->dmabuf = data->p[0].srcp_dma_buf;
410 dbgbuf->buflen = data->p[0].srcp_dma_buf->size;
411
412 dbgbuf->vaddr = NULL;
413 dbgbuf->width = buf->width;
414 dbgbuf->height = buf->height;
415
416 if (dbgbuf->dmabuf && (dbgbuf->buflen > 0)) {
417 dma_buf_begin_cpu_access(dbgbuf->dmabuf, 0, dbgbuf->buflen,
418 DMA_FROM_DEVICE);
419 dbgbuf->vaddr = dma_buf_kmap(dbgbuf->dmabuf, 0);
420 SDEROT_DBG("vaddr mapping: 0x%p/%ld w:%d/h:%d\n",
421 dbgbuf->vaddr, dbgbuf->buflen,
422 dbgbuf->width, dbgbuf->height);
423 }
424}
425
426/*
427 * sde_hw_rotator_unmap_vaddr - unmap the debug buffer from kernel space
428 * @dbgbuf: Pointer to debug buffer
429 */
430static void sde_hw_rotator_unmap_vaddr(struct sde_dbg_buf *dbgbuf)
431{
432 if (dbgbuf->vaddr) {
433 dma_buf_kunmap(dbgbuf->dmabuf, 0, dbgbuf->vaddr);
434 dma_buf_end_cpu_access(dbgbuf->dmabuf, 0, dbgbuf->buflen,
435 DMA_FROM_DEVICE);
436 }
437
438 dbgbuf->vaddr = NULL;
439 dbgbuf->dmabuf = NULL;
440 dbgbuf->buflen = 0;
441 dbgbuf->width = 0;
442 dbgbuf->height = 0;
443}
444
445/*
446 * sde_hw_rotator_setup_timestamp_packet - setup timestamp writeback command
447 * @ctx: Pointer to rotator context
448 * @mask: Bit mask location of the timestamp
449 * @swts: Software timestamp
450 */
451static void sde_hw_rotator_setup_timestamp_packet(
452 struct sde_hw_rotator_context *ctx, u32 mask, u32 swts)
453{
454 u32 *wrptr;
455
456 wrptr = sde_hw_rotator_get_regdma_segment(ctx);
457
458 /*
459 * Create a dummy packet write out to 1 location for timestamp
460 * generation.
461 */
462 SDE_REGDMA_BLKWRITE_INC(wrptr, ROT_SSPP_SRC_SIZE, 6);
463 SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x00010001);
464 SDE_REGDMA_BLKWRITE_DATA(wrptr, 0);
465 SDE_REGDMA_BLKWRITE_DATA(wrptr, 0);
466 SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x00010001);
467 SDE_REGDMA_BLKWRITE_DATA(wrptr, 0);
468 SDE_REGDMA_BLKWRITE_DATA(wrptr, ctx->ts_addr);
469 SDE_REGDMA_WRITE(wrptr, ROT_SSPP_SRC_YSTRIDE0, 4);
470 SDE_REGDMA_BLKWRITE_INC(wrptr, ROT_SSPP_SRC_FORMAT, 4);
471 SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x004037FF);
472 SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x03020100);
473 SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x80000000);
474 SDE_REGDMA_BLKWRITE_DATA(wrptr, ctx->timestamp);
Benjamin Chan15c93d82016-08-29 10:04:22 -0400475 /*
476 * Must clear secure buffer setting for SW timestamp because
477 * SW timstamp buffer allocation is always non-secure region.
478 */
479 if (ctx->is_secure) {
480 SDE_REGDMA_WRITE(wrptr, ROT_SSPP_SRC_ADDR_SW_STATUS, 0);
481 SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_ADDR_SW_STATUS, 0);
482 }
Alan Kwong9487de22016-01-16 22:06:36 -0500483 SDE_REGDMA_BLKWRITE_INC(wrptr, ROT_WB_DST_FORMAT, 4);
484 SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x000037FF);
485 SDE_REGDMA_BLKWRITE_DATA(wrptr, 0);
486 SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x03020100);
487 SDE_REGDMA_BLKWRITE_DATA(wrptr, ctx->ts_addr);
488 SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_YSTRIDE0, 4);
489 SDE_REGDMA_WRITE(wrptr, ROT_WB_OUT_SIZE, 0x00010001);
490 SDE_REGDMA_WRITE(wrptr, ROT_WB_OUT_IMG_SIZE, 0x00010001);
491 SDE_REGDMA_WRITE(wrptr, ROT_WB_OUT_XY, 0);
492 SDE_REGDMA_WRITE(wrptr, ROTTOP_DNSC, 0);
493 SDE_REGDMA_WRITE(wrptr, ROTTOP_OP_MODE, 1);
494 SDE_REGDMA_MODIFY(wrptr, REGDMA_TIMESTAMP_REG, mask, swts);
495 SDE_REGDMA_WRITE(wrptr, ROTTOP_START_CTRL, 1);
496
497 sde_hw_rotator_put_regdma_segment(ctx, wrptr);
498}
499
500/*
501 * sde_hw_rotator_setup_fetchengine - setup fetch engine
502 * @ctx: Pointer to rotator context
503 * @queue_id: Priority queue identifier
504 * @cfg: Fetch configuration
505 * @danger_lut: real-time QoS LUT for danger setting (not used)
506 * @safe_lut: real-time QoS LUT for safe setting (not used)
Benjamin Chanfb6faa32016-08-16 17:21:01 -0400507 * @dnsc_factor_w: downscale factor for width
508 * @dnsc_factor_h: downscale factor for height
Alan Kwong9487de22016-01-16 22:06:36 -0500509 * @flags: Control flag
510 */
511static void sde_hw_rotator_setup_fetchengine(struct sde_hw_rotator_context *ctx,
512 enum sde_rot_queue_prio queue_id,
513 struct sde_hw_rot_sspp_cfg *cfg, u32 danger_lut, u32 safe_lut,
Benjamin Chanfb6faa32016-08-16 17:21:01 -0400514 u32 dnsc_factor_w, u32 dnsc_factor_h, u32 flags)
Alan Kwong9487de22016-01-16 22:06:36 -0500515{
516 struct sde_hw_rotator *rot = ctx->rot;
517 struct sde_mdp_format_params *fmt;
518 struct sde_mdp_data *data;
Benjamin Chanfb6faa32016-08-16 17:21:01 -0400519 struct sde_rot_data_type *mdata = sde_rot_get_mdata();
Alan Kwong9487de22016-01-16 22:06:36 -0500520 u32 *wrptr;
521 u32 opmode = 0;
522 u32 chroma_samp = 0;
523 u32 src_format = 0;
524 u32 unpack = 0;
525 u32 width = cfg->img_width;
526 u32 height = cfg->img_height;
527 u32 fetch_blocksize = 0;
528 int i;
529
530 if (ctx->rot->mode == ROT_REGDMA_ON) {
531 SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_EN,
532 REGDMA_INT_MASK);
533 SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_OP_MODE,
534 REGDMA_EN);
535 }
536
537 wrptr = sde_hw_rotator_get_regdma_segment(ctx);
538
539 /* source image setup */
540 if ((flags & SDE_ROT_FLAG_DEINTERLACE)
541 && !(flags & SDE_ROT_FLAG_SOURCE_ROTATED_90)) {
542 for (i = 0; i < cfg->src_plane.num_planes; i++)
543 cfg->src_plane.ystride[i] *= 2;
544 width *= 2;
545 height /= 2;
546 }
547
548 /*
549 * REGDMA BLK write from SRC_SIZE to OP_MODE, total 15 registers
550 */
551 SDE_REGDMA_BLKWRITE_INC(wrptr, ROT_SSPP_SRC_SIZE, 15);
552
553 /* SRC_SIZE, SRC_IMG_SIZE, SRC_XY, OUT_SIZE, OUT_XY */
554 SDE_REGDMA_BLKWRITE_DATA(wrptr,
555 cfg->src_rect->w | (cfg->src_rect->h << 16));
556 SDE_REGDMA_BLKWRITE_DATA(wrptr, 0); /* SRC_IMG_SIZE unused */
557 SDE_REGDMA_BLKWRITE_DATA(wrptr,
558 cfg->src_rect->x | (cfg->src_rect->y << 16));
559 SDE_REGDMA_BLKWRITE_DATA(wrptr,
560 cfg->src_rect->w | (cfg->src_rect->h << 16));
561 SDE_REGDMA_BLKWRITE_DATA(wrptr,
562 cfg->src_rect->x | (cfg->src_rect->y << 16));
563
564 /* SRC_ADDR [0-3], SRC_YSTRIDE [0-1] */
565 data = cfg->data;
566 for (i = 0; i < SDE_ROT_MAX_PLANES; i++)
567 SDE_REGDMA_BLKWRITE_DATA(wrptr, data->p[i].addr);
568 SDE_REGDMA_BLKWRITE_DATA(wrptr, cfg->src_plane.ystride[0] |
569 (cfg->src_plane.ystride[1] << 16));
570 SDE_REGDMA_BLKWRITE_DATA(wrptr, cfg->src_plane.ystride[2] |
571 (cfg->src_plane.ystride[3] << 16));
572
573 /* UNUSED, write 0 */
574 SDE_REGDMA_BLKWRITE_DATA(wrptr, 0);
575
576 /* setup source format */
577 fmt = cfg->fmt;
578
579 chroma_samp = fmt->chroma_sample;
580 if (flags & SDE_ROT_FLAG_SOURCE_ROTATED_90) {
581 if (chroma_samp == SDE_MDP_CHROMA_H2V1)
582 chroma_samp = SDE_MDP_CHROMA_H1V2;
583 else if (chroma_samp == SDE_MDP_CHROMA_H1V2)
584 chroma_samp = SDE_MDP_CHROMA_H2V1;
585 }
586
587 src_format = (chroma_samp << 23) |
588 (fmt->fetch_planes << 19) |
589 (fmt->bits[C3_ALPHA] << 6) |
590 (fmt->bits[C2_R_Cr] << 4) |
591 (fmt->bits[C1_B_Cb] << 2) |
592 (fmt->bits[C0_G_Y] << 0);
593
594 if (fmt->alpha_enable &&
595 (fmt->fetch_planes == SDE_MDP_PLANE_INTERLEAVED))
596 src_format |= BIT(8); /* SRCC3_EN */
597
598 src_format |= ((fmt->unpack_count - 1) << 12) |
599 (fmt->unpack_tight << 17) |
600 (fmt->unpack_align_msb << 18) |
601 ((fmt->bpp - 1) << 9) |
602 ((fmt->frame_format & 3) << 30);
603
604 if (flags & SDE_ROT_FLAG_ROT_90)
605 src_format |= BIT(11); /* ROT90 */
606
607 if (sde_mdp_is_ubwc_format(fmt))
608 opmode |= BIT(0); /* BWC_DEC_EN */
609
610 /* if this is YUV pixel format, enable CSC */
611 if (sde_mdp_is_yuv_format(fmt))
612 src_format |= BIT(15); /* SRC_COLOR_SPACE */
613
614 if (fmt->pixel_mode == SDE_MDP_PIXEL_10BIT)
615 src_format |= BIT(14); /* UNPACK_DX_FORMAT */
616
617 /* SRC_FORMAT */
618 SDE_REGDMA_BLKWRITE_DATA(wrptr, src_format);
619
620 /* setup source unpack pattern */
621 unpack = (fmt->element[3] << 24) | (fmt->element[2] << 16) |
622 (fmt->element[1] << 8) | (fmt->element[0] << 0);
623
624 /* SRC_UNPACK_PATTERN */
625 SDE_REGDMA_BLKWRITE_DATA(wrptr, unpack);
626
627 /* setup source op mode */
628 if (flags & SDE_ROT_FLAG_FLIP_LR)
629 opmode |= BIT(13); /* FLIP_MODE L/R horizontal flip */
630 if (flags & SDE_ROT_FLAG_FLIP_UD)
631 opmode |= BIT(14); /* FLIP_MODE U/D vertical flip */
632 opmode |= BIT(31); /* MDSS_MDP_OP_PE_OVERRIDE */
633
634 /* SRC_OP_MODE */
635 SDE_REGDMA_BLKWRITE_DATA(wrptr, opmode);
636
637 /* setup source fetch config, TP10 uses different block size */
Benjamin Chanfb6faa32016-08-16 17:21:01 -0400638 if (test_bit(SDE_CAPS_R3_1P5_DOWNSCALE, mdata->sde_caps_map) &&
639 (dnsc_factor_w == 1) && (dnsc_factor_h == 1)) {
640 if (sde_mdp_is_tp10_format(fmt))
641 fetch_blocksize = SDE_ROT_SSPP_FETCH_BLOCKSIZE_144_EXT;
642 else
643 fetch_blocksize = SDE_ROT_SSPP_FETCH_BLOCKSIZE_192_EXT;
644 } else {
645 if (sde_mdp_is_tp10_format(fmt))
646 fetch_blocksize = SDE_ROT_SSPP_FETCH_BLOCKSIZE_96;
647 else
648 fetch_blocksize = SDE_ROT_SSPP_FETCH_BLOCKSIZE_128;
649 }
650
Alan Kwong9487de22016-01-16 22:06:36 -0500651 SDE_REGDMA_WRITE(wrptr, ROT_SSPP_FETCH_CONFIG,
652 fetch_blocksize |
653 SDE_ROT_SSPP_FETCH_CONFIG_RESET_VALUE |
654 ((rot->highest_bank & 0x3) << 18));
655
656 /* setup source buffer plane security status */
Abhijit Kulkarni298c8232016-09-26 22:32:10 -0700657 if (flags & (SDE_ROT_FLAG_SECURE_OVERLAY_SESSION |
658 SDE_ROT_FLAG_SECURE_CAMERA_SESSION)) {
Alan Kwong9487de22016-01-16 22:06:36 -0500659 SDE_REGDMA_WRITE(wrptr, ROT_SSPP_SRC_ADDR_SW_STATUS, 0xF);
660 ctx->is_secure = true;
Benjamin Chan15c93d82016-08-29 10:04:22 -0400661 } else {
662 SDE_REGDMA_WRITE(wrptr, ROT_SSPP_SRC_ADDR_SW_STATUS, 0);
663 ctx->is_secure = false;
Alan Kwong9487de22016-01-16 22:06:36 -0500664 }
665
Benjamin Chan99eb63b2016-12-21 15:45:26 -0500666 /*
667 * Determine if traffic shaping is required. Only enable traffic
668 * shaping when content is 4k@30fps. The actual traffic shaping
669 * bandwidth calculation is done in output setup.
670 */
671 if (((cfg->src_rect->w * cfg->src_rect->h) >= RES_UHD) &&
672 (cfg->fps <= 30)) {
673 SDEROT_DBG("Enable Traffic Shaper\n");
674 ctx->is_traffic_shaping = true;
675 } else {
676 SDEROT_DBG("Disable Traffic Shaper\n");
677 ctx->is_traffic_shaping = false;
678 }
679
Alan Kwong9487de22016-01-16 22:06:36 -0500680 /* Update command queue write ptr */
681 sde_hw_rotator_put_regdma_segment(ctx, wrptr);
682}
683
684/*
685 * sde_hw_rotator_setup_wbengine - setup writeback engine
686 * @ctx: Pointer to rotator context
687 * @queue_id: Priority queue identifier
688 * @cfg: Writeback configuration
689 * @flags: Control flag
690 */
691static void sde_hw_rotator_setup_wbengine(struct sde_hw_rotator_context *ctx,
692 enum sde_rot_queue_prio queue_id,
693 struct sde_hw_rot_wb_cfg *cfg,
694 u32 flags)
695{
696 struct sde_mdp_format_params *fmt;
697 u32 *wrptr;
698 u32 pack = 0;
699 u32 dst_format = 0;
700 int i;
701
702 wrptr = sde_hw_rotator_get_regdma_segment(ctx);
703
704 fmt = cfg->fmt;
705
706 /* setup WB DST format */
707 dst_format |= (fmt->chroma_sample << 23) |
708 (fmt->fetch_planes << 19) |
709 (fmt->bits[C3_ALPHA] << 6) |
710 (fmt->bits[C2_R_Cr] << 4) |
711 (fmt->bits[C1_B_Cb] << 2) |
712 (fmt->bits[C0_G_Y] << 0);
713
714 /* alpha control */
715 if (fmt->bits[C3_ALPHA] || fmt->alpha_enable) {
716 dst_format |= BIT(8);
717 if (!fmt->alpha_enable) {
718 dst_format |= BIT(14);
719 SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_ALPHA_X_VALUE, 0);
720 }
721 }
722
723 dst_format |= ((fmt->unpack_count - 1) << 12) |
724 (fmt->unpack_tight << 17) |
725 (fmt->unpack_align_msb << 18) |
726 ((fmt->bpp - 1) << 9) |
727 ((fmt->frame_format & 3) << 30);
728
729 if (sde_mdp_is_yuv_format(fmt))
730 dst_format |= BIT(15);
731
732 if (fmt->pixel_mode == SDE_MDP_PIXEL_10BIT)
733 dst_format |= BIT(21); /* PACK_DX_FORMAT */
734
735 /*
736 * REGDMA BLK write, from DST_FORMAT to DST_YSTRIDE 1, total 9 regs
737 */
738 SDE_REGDMA_BLKWRITE_INC(wrptr, ROT_WB_DST_FORMAT, 9);
739
740 /* DST_FORMAT */
741 SDE_REGDMA_BLKWRITE_DATA(wrptr, dst_format);
742
743 /* DST_OP_MODE */
744 if (sde_mdp_is_ubwc_format(fmt))
745 SDE_REGDMA_BLKWRITE_DATA(wrptr, BIT(0));
746 else
747 SDE_REGDMA_BLKWRITE_DATA(wrptr, 0);
748
749 /* DST_PACK_PATTERN */
750 pack = (fmt->element[3] << 24) | (fmt->element[2] << 16) |
751 (fmt->element[1] << 8) | (fmt->element[0] << 0);
752 SDE_REGDMA_BLKWRITE_DATA(wrptr, pack);
753
754 /* DST_ADDR [0-3], DST_YSTRIDE [0-1] */
755 for (i = 0; i < SDE_ROT_MAX_PLANES; i++)
756 SDE_REGDMA_BLKWRITE_DATA(wrptr, cfg->data->p[i].addr);
757 SDE_REGDMA_BLKWRITE_DATA(wrptr, cfg->dst_plane.ystride[0] |
758 (cfg->dst_plane.ystride[1] << 16));
759 SDE_REGDMA_BLKWRITE_DATA(wrptr, cfg->dst_plane.ystride[2] |
760 (cfg->dst_plane.ystride[3] << 16));
761
762 /* setup WB out image size and ROI */
763 SDE_REGDMA_WRITE(wrptr, ROT_WB_OUT_IMG_SIZE,
764 cfg->img_width | (cfg->img_height << 16));
765 SDE_REGDMA_WRITE(wrptr, ROT_WB_OUT_SIZE,
766 cfg->dst_rect->w | (cfg->dst_rect->h << 16));
767 SDE_REGDMA_WRITE(wrptr, ROT_WB_OUT_XY,
768 cfg->dst_rect->x | (cfg->dst_rect->y << 16));
769
Abhijit Kulkarni298c8232016-09-26 22:32:10 -0700770 if (flags & (SDE_ROT_FLAG_SECURE_OVERLAY_SESSION |
771 SDE_ROT_FLAG_SECURE_CAMERA_SESSION))
Benjamin Chan15c93d82016-08-29 10:04:22 -0400772 SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_ADDR_SW_STATUS, 0x1);
773 else
774 SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_ADDR_SW_STATUS, 0);
775
Alan Kwong9487de22016-01-16 22:06:36 -0500776 /*
777 * setup Downscale factor
778 */
779 SDE_REGDMA_WRITE(wrptr, ROTTOP_DNSC,
780 cfg->v_downscale_factor |
781 (cfg->h_downscale_factor << 16));
782
783 /* write config setup for bank configration */
784 SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_WRITE_CONFIG,
785 (ctx->rot->highest_bank & 0x3) << 8);
786
787 if (flags & SDE_ROT_FLAG_ROT_90)
788 SDE_REGDMA_WRITE(wrptr, ROTTOP_OP_MODE, 0x3);
789 else
790 SDE_REGDMA_WRITE(wrptr, ROTTOP_OP_MODE, 0x1);
791
Benjamin Chan99eb63b2016-12-21 15:45:26 -0500792 /* setup traffic shaper for 4k 30fps content */
793 if (ctx->is_traffic_shaping) {
794 u32 bw;
795
796 /*
797 * Target to finish in 12ms, and we need to set number of bytes
798 * per clock tick for traffic shaping.
799 * Each clock tick run @ 19.2MHz, so we need we know total of
800 * clock ticks in 14ms, i.e. 12ms/(1/19.2MHz) ==> 23040
801 * Finally, calcualte the byte count per clock tick based on
802 * resolution, bpp and compression ratio.
803 */
804 bw = cfg->dst_rect->w * cfg->dst_rect->h;
805
806 if (fmt->chroma_sample == SDE_MDP_CHROMA_420)
807 bw = (bw * 3) / 2;
808 else
809 bw *= fmt->bpp;
810
811 bw /= TRAFFIC_SHAPE_CLKTICK_12MS;
812 if (bw > 0xFF)
813 bw = 0xFF;
814 SDE_REGDMA_WRITE(wrptr, ROT_WB_TRAFFIC_SHAPER_WR_CLIENT,
815 BIT(31) | bw);
816 SDEROT_DBG("Enable ROT_WB Traffic Shaper:%d\n", bw);
817 } else {
818 SDE_REGDMA_WRITE(wrptr, ROT_WB_TRAFFIC_SHAPER_WR_CLIENT, 0);
819 SDEROT_DBG("Disable ROT_WB Traffic Shaper\n");
820 }
821
Alan Kwong9487de22016-01-16 22:06:36 -0500822 /* Update command queue write ptr */
823 sde_hw_rotator_put_regdma_segment(ctx, wrptr);
824}
825
826/*
827 * sde_hw_rotator_start_no_regdma - start non-regdma operation
828 * @ctx: Pointer to rotator context
829 * @queue_id: Priority queue identifier
830 */
831static u32 sde_hw_rotator_start_no_regdma(struct sde_hw_rotator_context *ctx,
832 enum sde_rot_queue_prio queue_id)
833{
834 struct sde_hw_rotator *rot = ctx->rot;
835 u32 *wrptr;
836 u32 *rdptr;
837 u8 *addr;
838 u32 mask;
839 u32 blksize;
840
841 rdptr = sde_hw_rotator_get_regdma_segment_base(ctx);
842 wrptr = sde_hw_rotator_get_regdma_segment(ctx);
843
844 if (rot->irq_num >= 0) {
845 SDE_REGDMA_WRITE(wrptr, ROTTOP_INTR_EN, 1);
846 SDE_REGDMA_WRITE(wrptr, ROTTOP_INTR_CLEAR, 1);
847 reinit_completion(&ctx->rot_comp);
Alan Kwong818b7fc2016-07-24 22:07:41 -0400848 sde_hw_rotator_enable_irq(rot);
Alan Kwong9487de22016-01-16 22:06:36 -0500849 }
850
851 SDE_REGDMA_WRITE(wrptr, ROTTOP_START_CTRL, 1);
852
853 /* Update command queue write ptr */
854 sde_hw_rotator_put_regdma_segment(ctx, wrptr);
855
856 SDEROT_DBG("BEGIN %d\n", ctx->timestamp);
857 /* Write all command stream to Rotator blocks */
858 /* Rotator will start right away after command stream finish writing */
859 while (rdptr < wrptr) {
860 u32 op = REGDMA_OP_MASK & *rdptr;
861
862 switch (op) {
863 case REGDMA_OP_NOP:
864 SDEROT_DBG("NOP\n");
865 rdptr++;
866 break;
867 case REGDMA_OP_REGWRITE:
868 SDEROT_DBG("REGW %6.6x %8.8x\n",
869 rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
870 rdptr[1]);
871 addr = rot->mdss_base +
872 (*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
873 writel_relaxed(*rdptr++, addr);
874 break;
875 case REGDMA_OP_REGMODIFY:
876 SDEROT_DBG("REGM %6.6x %8.8x %8.8x\n",
877 rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
878 rdptr[1], rdptr[2]);
879 addr = rot->mdss_base +
880 (*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
881 mask = *rdptr++;
882 writel_relaxed((readl_relaxed(addr) & mask) | *rdptr++,
883 addr);
884 break;
885 case REGDMA_OP_BLKWRITE_SINGLE:
886 SDEROT_DBG("BLKWS %6.6x %6.6x\n",
887 rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
888 rdptr[1]);
889 addr = rot->mdss_base +
890 (*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
891 blksize = *rdptr++;
892 while (blksize--) {
893 SDEROT_DBG("DATA %8.8x\n", rdptr[0]);
894 writel_relaxed(*rdptr++, addr);
895 }
896 break;
897 case REGDMA_OP_BLKWRITE_INC:
898 SDEROT_DBG("BLKWI %6.6x %6.6x\n",
899 rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
900 rdptr[1]);
901 addr = rot->mdss_base +
902 (*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
903 blksize = *rdptr++;
904 while (blksize--) {
905 SDEROT_DBG("DATA %8.8x\n", rdptr[0]);
906 writel_relaxed(*rdptr++, addr);
907 addr += 4;
908 }
909 break;
910 default:
911 /* Other not supported OP mode
912 * Skip data for now for unregonized OP mode
913 */
914 SDEROT_DBG("UNDEFINED\n");
915 rdptr++;
916 break;
917 }
918 }
919 SDEROT_DBG("END %d\n", ctx->timestamp);
920
921 return ctx->timestamp;
922}
923
924/*
925 * sde_hw_rotator_start_regdma - start regdma operation
926 * @ctx: Pointer to rotator context
927 * @queue_id: Priority queue identifier
928 */
929static u32 sde_hw_rotator_start_regdma(struct sde_hw_rotator_context *ctx,
930 enum sde_rot_queue_prio queue_id)
931{
932 struct sde_hw_rotator *rot = ctx->rot;
933 u32 *wrptr;
934 u32 regdmaSlot;
935 u32 offset;
936 long length;
937 long ts_length;
938 u32 enableInt;
939 u32 swts = 0;
940 u32 mask = 0;
941
942 wrptr = sde_hw_rotator_get_regdma_segment(ctx);
943
Alan Kwong9487de22016-01-16 22:06:36 -0500944 /*
945 * Last ROT command must be ROT_START before REGDMA start
946 */
947 SDE_REGDMA_WRITE(wrptr, ROTTOP_START_CTRL, 1);
948 sde_hw_rotator_put_regdma_segment(ctx, wrptr);
949
950 /*
951 * Start REGDMA with command offset and size
952 */
953 regdmaSlot = sde_hw_rotator_get_regdma_ctxidx(ctx);
954 length = ((long)wrptr - (long)ctx->regdma_base) / 4;
955 offset = (u32)(ctx->regdma_base - (u32 *)(rot->mdss_base +
956 REGDMA_RAM_REGDMA_CMD_RAM));
957 enableInt = ((ctx->timestamp & 1) + 1) << 30;
958
959 SDEROT_DBG(
960 "regdma(%d)[%d] <== INT:0x%X|length:%ld|offset:0x%X, ts:%X\n",
961 queue_id, regdmaSlot, enableInt, length, offset,
962 ctx->timestamp);
963
964 /* ensure the command packet is issued before the submit command */
965 wmb();
966
967 /* REGDMA submission for current context */
968 if (queue_id == ROT_QUEUE_HIGH_PRIORITY) {
969 SDE_ROTREG_WRITE(rot->mdss_base,
970 REGDMA_CSR_REGDMA_QUEUE_0_SUBMIT,
971 (length << 14) | offset);
972 swts = ctx->timestamp;
973 mask = ~SDE_REGDMA_SWTS_MASK;
974 } else {
975 SDE_ROTREG_WRITE(rot->mdss_base,
976 REGDMA_CSR_REGDMA_QUEUE_1_SUBMIT,
977 (length << 14) | offset);
978 swts = ctx->timestamp << SDE_REGDMA_SWTS_SHIFT;
979 mask = ~(SDE_REGDMA_SWTS_MASK << SDE_REGDMA_SWTS_SHIFT);
980 }
981
982 /* Write timestamp after previous rotator job finished */
983 sde_hw_rotator_setup_timestamp_packet(ctx, mask, swts);
984 offset += length;
985 ts_length = sde_hw_rotator_get_regdma_segment(ctx) - wrptr;
986 WARN_ON((length + ts_length) > SDE_HW_ROT_REGDMA_SEG_SIZE);
987
988 /* ensure command packet is issue before the submit command */
989 wmb();
990
991 if (queue_id == ROT_QUEUE_HIGH_PRIORITY) {
992 SDE_ROTREG_WRITE(rot->mdss_base,
993 REGDMA_CSR_REGDMA_QUEUE_0_SUBMIT,
994 enableInt | (ts_length << 14) | offset);
995 } else {
996 SDE_ROTREG_WRITE(rot->mdss_base,
997 REGDMA_CSR_REGDMA_QUEUE_1_SUBMIT,
998 enableInt | (ts_length << 14) | offset);
999 }
1000
Alan Kwong9487de22016-01-16 22:06:36 -05001001 /* Update command queue write ptr */
1002 sde_hw_rotator_put_regdma_segment(ctx, wrptr);
1003
1004 return ctx->timestamp;
1005}
1006
1007/*
1008 * sde_hw_rotator_wait_done_no_regdma - wait for non-regdma completion
1009 * @ctx: Pointer to rotator context
1010 * @queue_id: Priority queue identifier
1011 * @flags: Option flag
1012 */
1013static u32 sde_hw_rotator_wait_done_no_regdma(
1014 struct sde_hw_rotator_context *ctx,
1015 enum sde_rot_queue_prio queue_id, u32 flag)
1016{
1017 struct sde_hw_rotator *rot = ctx->rot;
1018 int rc = 0;
1019 u32 sts = 0;
1020 u32 status;
1021 unsigned long flags;
1022
1023 if (rot->irq_num >= 0) {
1024 SDEROT_DBG("Wait for Rotator completion\n");
1025 rc = wait_for_completion_timeout(&ctx->rot_comp,
1026 KOFF_TIMEOUT);
1027
1028 spin_lock_irqsave(&rot->rotisr_lock, flags);
1029 status = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_STATUS);
1030 if (rc == 0) {
1031 /*
1032 * Timeout, there might be error,
1033 * or rotator still busy
1034 */
1035 if (status & ROT_BUSY_BIT)
1036 SDEROT_ERR(
1037 "Timeout waiting for rotator done\n");
1038 else if (status & ROT_ERROR_BIT)
1039 SDEROT_ERR(
1040 "Rotator report error status\n");
1041 else
1042 SDEROT_WARN(
1043 "Timeout waiting, but rotator job is done!!\n");
1044
Alan Kwong818b7fc2016-07-24 22:07:41 -04001045 sde_hw_rotator_disable_irq(rot);
Alan Kwong9487de22016-01-16 22:06:36 -05001046 }
1047 spin_unlock_irqrestore(&rot->rotisr_lock, flags);
1048 } else {
1049 int cnt = 200;
1050
1051 do {
1052 udelay(500);
1053 status = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_STATUS);
1054 cnt--;
1055 } while ((cnt > 0) && (status & ROT_BUSY_BIT)
1056 && ((status & ROT_ERROR_BIT) == 0));
1057
1058 if (status & ROT_ERROR_BIT)
1059 SDEROT_ERR("Rotator error\n");
1060 else if (status & ROT_BUSY_BIT)
1061 SDEROT_ERR("Rotator busy\n");
1062
1063 SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_CLEAR,
1064 ROT_DONE_CLEAR);
1065 }
1066
1067 sts = (status & ROT_ERROR_BIT) ? -ENODEV : 0;
1068
1069 return sts;
1070}
1071
1072/*
1073 * sde_hw_rotator_wait_done_regdma - wait for regdma completion
1074 * @ctx: Pointer to rotator context
1075 * @queue_id: Priority queue identifier
1076 * @flags: Option flag
1077 */
1078static u32 sde_hw_rotator_wait_done_regdma(
1079 struct sde_hw_rotator_context *ctx,
1080 enum sde_rot_queue_prio queue_id, u32 flag)
1081{
1082 struct sde_hw_rotator *rot = ctx->rot;
1083 int rc = 0;
1084 u32 status;
1085 u32 last_isr;
1086 u32 last_ts;
1087 u32 int_id;
Alan Kwong818b7fc2016-07-24 22:07:41 -04001088 u32 swts;
Alan Kwong9487de22016-01-16 22:06:36 -05001089 u32 sts = 0;
Alan Kwong9487de22016-01-16 22:06:36 -05001090 unsigned long flags;
1091
1092 if (rot->irq_num >= 0) {
1093 SDEROT_DBG("Wait for REGDMA completion, ctx:%p, ts:%X\n",
1094 ctx, ctx->timestamp);
Alan Kwong818b7fc2016-07-24 22:07:41 -04001095 rc = wait_event_timeout(ctx->regdma_waitq,
1096 !sde_hw_rotator_pending_swts(rot, ctx, &swts),
Alan Kwong9487de22016-01-16 22:06:36 -05001097 KOFF_TIMEOUT);
1098
Benjamin Chane7ca72e2016-12-22 18:42:34 -05001099 ATRACE_INT("sde_rot_done", 0);
Alan Kwong9487de22016-01-16 22:06:36 -05001100 spin_lock_irqsave(&rot->rotisr_lock, flags);
1101
1102 last_isr = ctx->last_regdma_isr_status;
1103 last_ts = ctx->last_regdma_timestamp;
1104 status = last_isr & REGDMA_INT_MASK;
1105 int_id = last_ts & 1;
1106 SDEROT_DBG("INT status:0x%X, INT id:%d, timestamp:0x%X\n",
1107 status, int_id, last_ts);
1108
1109 if (rc == 0 || (status & REGDMA_INT_ERR_MASK)) {
Alan Kwong818b7fc2016-07-24 22:07:41 -04001110 bool pending;
1111
1112 pending = sde_hw_rotator_pending_swts(rot, ctx, &swts);
Alan Kwong9487de22016-01-16 22:06:36 -05001113 SDEROT_ERR(
Alan Kwong818b7fc2016-07-24 22:07:41 -04001114 "Timeout wait for regdma interrupt status, ts:0x%X/0x%X pending:%d\n",
1115 ctx->timestamp, swts, pending);
Alan Kwong9487de22016-01-16 22:06:36 -05001116
1117 if (status & REGDMA_WATCHDOG_INT)
1118 SDEROT_ERR("REGDMA watchdog interrupt\n");
1119 else if (status & REGDMA_INVALID_DESCRIPTOR)
1120 SDEROT_ERR("REGDMA invalid descriptor\n");
1121 else if (status & REGDMA_INCOMPLETE_CMD)
1122 SDEROT_ERR("REGDMA incomplete command\n");
1123 else if (status & REGDMA_INVALID_CMD)
1124 SDEROT_ERR("REGDMA invalid command\n");
1125
Alan Kwong818b7fc2016-07-24 22:07:41 -04001126 sde_hw_rotator_dump_status(rot);
Alan Kwong9487de22016-01-16 22:06:36 -05001127 status = ROT_ERROR_BIT;
Alan Kwong818b7fc2016-07-24 22:07:41 -04001128 } else {
1129 if (rc == 1)
1130 SDEROT_WARN(
1131 "REGDMA done but no irq, ts:0x%X/0x%X\n",
1132 ctx->timestamp, swts);
Alan Kwong9487de22016-01-16 22:06:36 -05001133 status = 0;
1134 }
1135
Alan Kwong9487de22016-01-16 22:06:36 -05001136 spin_unlock_irqrestore(&rot->rotisr_lock, flags);
1137 } else {
1138 int cnt = 200;
Alan Kwongb0679602016-11-27 17:04:13 -08001139 bool pending;
Alan Kwong9487de22016-01-16 22:06:36 -05001140
1141 do {
1142 udelay(500);
Alan Kwongb0679602016-11-27 17:04:13 -08001143 last_isr = SDE_ROTREG_READ(rot->mdss_base,
1144 REGDMA_CSR_REGDMA_INT_STATUS);
1145 pending = sde_hw_rotator_pending_swts(rot, ctx, &swts);
Alan Kwong9487de22016-01-16 22:06:36 -05001146 cnt--;
Alan Kwongb0679602016-11-27 17:04:13 -08001147 } while ((cnt > 0) && pending &&
1148 ((last_isr & REGDMA_INT_ERR_MASK) == 0));
Alan Kwong9487de22016-01-16 22:06:36 -05001149
Alan Kwongb0679602016-11-27 17:04:13 -08001150 if (last_isr & REGDMA_INT_ERR_MASK) {
1151 SDEROT_ERR("Rotator error, ts:0x%X/0x%X status:%x\n",
1152 ctx->timestamp, swts, last_isr);
1153 sde_hw_rotator_dump_status(rot);
1154 status = ROT_ERROR_BIT;
1155 } else if (pending) {
1156 SDEROT_ERR("Rotator timeout, ts:0x%X/0x%X status:%x\n",
1157 ctx->timestamp, swts, last_isr);
1158 sde_hw_rotator_dump_status(rot);
1159 status = ROT_ERROR_BIT;
1160 } else {
1161 status = 0;
1162 }
Alan Kwong9487de22016-01-16 22:06:36 -05001163
1164 SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_CLEAR,
Alan Kwongb0679602016-11-27 17:04:13 -08001165 last_isr);
Alan Kwong9487de22016-01-16 22:06:36 -05001166 }
1167
1168 sts = (status & ROT_ERROR_BIT) ? -ENODEV : 0;
1169
Benjamin Chan4ec1f1d2016-09-15 22:49:49 -04001170 if (status & ROT_ERROR_BIT)
1171 SDEROT_EVTLOG_TOUT_HANDLER("rot", "vbif_dbg_bus", "panic");
1172
Alan Kwong9487de22016-01-16 22:06:36 -05001173 return sts;
1174}
1175
1176/*
1177 * setup_rotator_ops - setup callback functions for the low-level HAL
1178 * @ops: Pointer to low-level ops callback
1179 * @mode: Operation mode (non-regdma or regdma)
1180 */
1181static void setup_rotator_ops(struct sde_hw_rotator_ops *ops,
1182 enum sde_rotator_regdma_mode mode)
1183{
1184 ops->setup_rotator_fetchengine = sde_hw_rotator_setup_fetchengine;
1185 ops->setup_rotator_wbengine = sde_hw_rotator_setup_wbengine;
1186 if (mode == ROT_REGDMA_ON) {
1187 ops->start_rotator = sde_hw_rotator_start_regdma;
1188 ops->wait_rotator_done = sde_hw_rotator_wait_done_regdma;
1189 } else {
1190 ops->start_rotator = sde_hw_rotator_start_no_regdma;
1191 ops->wait_rotator_done = sde_hw_rotator_wait_done_no_regdma;
1192 }
1193}
1194
1195/*
1196 * sde_hw_rotator_swts_create - create software timestamp buffer
1197 * @rot: Pointer to rotator hw
1198 *
1199 * This buffer is used by regdma to keep track of last completed command.
1200 */
1201static int sde_hw_rotator_swts_create(struct sde_hw_rotator *rot)
1202{
1203 int rc = 0;
1204 struct ion_handle *handle;
1205 struct sde_mdp_img_data *data;
Abhijit Kulkarni298c8232016-09-26 22:32:10 -07001206 struct sde_rot_data_type *mdata = sde_rot_get_mdata();
Alan Kwong9487de22016-01-16 22:06:36 -05001207 u32 bufsize = sizeof(int) * SDE_HW_ROT_REGDMA_TOTAL_CTX * 2;
1208
Abhijit Kulkarni298c8232016-09-26 22:32:10 -07001209 rot->iclient = mdata->iclient;
Alan Kwong9487de22016-01-16 22:06:36 -05001210
1211 handle = ion_alloc(rot->iclient, bufsize, SZ_4K,
1212 ION_HEAP(ION_SYSTEM_HEAP_ID), 0);
1213 if (IS_ERR_OR_NULL(handle)) {
1214 SDEROT_ERR("ion memory allocation failed\n");
1215 return -ENOMEM;
1216 }
1217
1218 data = &rot->swts_buf;
1219 data->len = bufsize;
1220 data->srcp_dma_buf = ion_share_dma_buf(rot->iclient, handle);
1221 if (IS_ERR(data->srcp_dma_buf)) {
1222 SDEROT_ERR("ion_dma_buf setup failed\n");
1223 rc = -ENOMEM;
1224 goto imap_err;
1225 }
1226
1227 sde_smmu_ctrl(1);
1228
1229 data->srcp_attachment = sde_smmu_dma_buf_attach(data->srcp_dma_buf,
1230 &rot->pdev->dev, SDE_IOMMU_DOMAIN_ROT_UNSECURE);
1231 if (IS_ERR_OR_NULL(data->srcp_attachment)) {
1232 SDEROT_ERR("sde_smmu_dma_buf_attach error\n");
1233 rc = -ENOMEM;
1234 goto err_put;
1235 }
1236
1237 data->srcp_table = dma_buf_map_attachment(data->srcp_attachment,
1238 DMA_BIDIRECTIONAL);
1239 if (IS_ERR_OR_NULL(data->srcp_table)) {
1240 SDEROT_ERR("dma_buf_map_attachment error\n");
1241 rc = -ENOMEM;
1242 goto err_detach;
1243 }
1244
1245 rc = sde_smmu_map_dma_buf(data->srcp_dma_buf, data->srcp_table,
1246 SDE_IOMMU_DOMAIN_ROT_UNSECURE, &data->addr,
1247 &data->len, DMA_BIDIRECTIONAL);
1248 if (IS_ERR_VALUE(rc)) {
1249 SDEROT_ERR("smmu_map_dma_buf failed: (%d)\n", rc);
1250 goto err_unmap;
1251 }
1252
1253 dma_buf_begin_cpu_access(data->srcp_dma_buf, 0, data->len,
1254 DMA_FROM_DEVICE);
1255 rot->swts_buffer = dma_buf_kmap(data->srcp_dma_buf, 0);
1256 if (IS_ERR_OR_NULL(rot->swts_buffer)) {
1257 SDEROT_ERR("ion kernel memory mapping failed\n");
1258 rc = IS_ERR(rot->swts_buffer);
1259 goto kmap_err;
1260 }
1261
1262 data->mapped = true;
1263 SDEROT_DBG("swts buffer mapped: %pad/%lx va:%p\n", &data->addr,
1264 data->len, rot->swts_buffer);
1265
1266 ion_free(rot->iclient, handle);
1267
1268 sde_smmu_ctrl(0);
1269
1270 return rc;
1271kmap_err:
1272 sde_smmu_unmap_dma_buf(data->srcp_table, SDE_IOMMU_DOMAIN_ROT_UNSECURE,
1273 DMA_FROM_DEVICE, data->srcp_dma_buf);
1274err_unmap:
1275 dma_buf_unmap_attachment(data->srcp_attachment, data->srcp_table,
1276 DMA_FROM_DEVICE);
1277err_detach:
1278 dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment);
1279err_put:
1280 dma_buf_put(data->srcp_dma_buf);
1281 data->srcp_dma_buf = NULL;
1282imap_err:
1283 ion_free(rot->iclient, handle);
1284
1285 return rc;
1286}
1287
1288/*
1289 * sde_hw_rotator_swtc_destroy - destroy software timestamp buffer
1290 * @rot: Pointer to rotator hw
1291 */
1292static void sde_hw_rotator_swtc_destroy(struct sde_hw_rotator *rot)
1293{
1294 struct sde_mdp_img_data *data;
1295
1296 data = &rot->swts_buf;
1297
1298 dma_buf_end_cpu_access(data->srcp_dma_buf, 0, data->len,
1299 DMA_FROM_DEVICE);
1300 dma_buf_kunmap(data->srcp_dma_buf, 0, rot->swts_buffer);
1301
1302 sde_smmu_unmap_dma_buf(data->srcp_table, SDE_IOMMU_DOMAIN_ROT_UNSECURE,
1303 DMA_FROM_DEVICE, data->srcp_dma_buf);
1304 dma_buf_unmap_attachment(data->srcp_attachment, data->srcp_table,
1305 DMA_FROM_DEVICE);
1306 dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment);
1307 dma_buf_put(data->srcp_dma_buf);
1308 data->srcp_dma_buf = NULL;
1309}
1310
1311/*
Benjamin Chan0f9e61d2016-09-16 16:01:09 -04001312 * sde_hw_rotator_pre_pmevent - SDE rotator core will call this before a
1313 * PM event occurs
1314 * @mgr: Pointer to rotator manager
1315 * @pmon: Boolean indicate an on/off power event
1316 */
1317void sde_hw_rotator_pre_pmevent(struct sde_rot_mgr *mgr, bool pmon)
1318{
1319 struct sde_hw_rotator *rot;
1320 u32 l_ts, h_ts, swts, hwts;
1321 u32 rotsts, regdmasts;
1322
1323 /*
1324 * Check last HW timestamp with SW timestamp before power off event.
1325 * If there is a mismatch, that will be quite possible the rotator HW
1326 * is either hang or not finishing last submitted job. In that case,
1327 * it is best to do a timeout eventlog to capture some good events
1328 * log data for analysis.
1329 */
1330 if (!pmon && mgr && mgr->hw_data) {
1331 rot = mgr->hw_data;
1332 h_ts = atomic_read(&rot->timestamp[ROT_QUEUE_HIGH_PRIORITY]);
1333 l_ts = atomic_read(&rot->timestamp[ROT_QUEUE_LOW_PRIORITY]);
1334
1335 /* contruct the combined timstamp */
1336 swts = (h_ts & SDE_REGDMA_SWTS_MASK) |
1337 ((l_ts & SDE_REGDMA_SWTS_MASK) <<
1338 SDE_REGDMA_SWTS_SHIFT);
1339
1340 /* Need to turn on clock to access rotator register */
1341 sde_rotator_clk_ctrl(mgr, true);
1342 hwts = SDE_ROTREG_READ(rot->mdss_base, REGDMA_TIMESTAMP_REG);
1343 regdmasts = SDE_ROTREG_READ(rot->mdss_base,
1344 REGDMA_CSR_REGDMA_BLOCK_STATUS);
1345 rotsts = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_STATUS);
1346
1347 SDEROT_DBG(
1348 "swts:0x%x, hwts:0x%x, regdma-sts:0x%x, rottop-sts:0x%x\n",
1349 swts, hwts, regdmasts, rotsts);
1350 SDEROT_EVTLOG(swts, hwts, regdmasts, rotsts);
1351
1352 if ((swts != hwts) && ((regdmasts & REGDMA_BUSY) ||
1353 (rotsts & ROT_STATUS_MASK))) {
1354 SDEROT_ERR(
1355 "Mismatch SWTS with HWTS: swts:0x%x, hwts:0x%x, regdma-sts:0x%x, rottop-sts:0x%x\n",
1356 swts, hwts, regdmasts, rotsts);
1357 SDEROT_EVTLOG_TOUT_HANDLER("rot", "vbif_dbg_bus",
1358 "panic");
1359 }
1360
1361 /* Turn off rotator clock after checking rotator registers */
1362 sde_rotator_clk_ctrl(mgr, false);
1363 }
1364}
1365
1366/*
1367 * sde_hw_rotator_post_pmevent - SDE rotator core will call this after a
1368 * PM event occurs
1369 * @mgr: Pointer to rotator manager
1370 * @pmon: Boolean indicate an on/off power event
1371 */
1372void sde_hw_rotator_post_pmevent(struct sde_rot_mgr *mgr, bool pmon)
1373{
1374 struct sde_hw_rotator *rot;
1375 u32 l_ts, h_ts, swts;
1376
1377 /*
1378 * After a power on event, the rotator HW is reset to default setting.
1379 * It is necessary to synchronize the SW timestamp with the HW.
1380 */
1381 if (pmon && mgr && mgr->hw_data) {
1382 rot = mgr->hw_data;
1383 h_ts = atomic_read(&rot->timestamp[ROT_QUEUE_HIGH_PRIORITY]);
1384 l_ts = atomic_read(&rot->timestamp[ROT_QUEUE_LOW_PRIORITY]);
1385
1386 /* contruct the combined timstamp */
1387 swts = (h_ts & SDE_REGDMA_SWTS_MASK) |
1388 ((l_ts & SDE_REGDMA_SWTS_MASK) <<
1389 SDE_REGDMA_SWTS_SHIFT);
1390
1391 SDEROT_DBG("swts:0x%x, h_ts:0x%x, l_ts;0x%x\n",
1392 swts, h_ts, l_ts);
1393 SDEROT_EVTLOG(swts, h_ts, l_ts);
1394 rot->reset_hw_ts = true;
1395 rot->last_hw_ts = swts;
1396 }
1397}
1398
1399/*
Alan Kwong9487de22016-01-16 22:06:36 -05001400 * sde_hw_rotator_destroy - Destroy hw rotator and free allocated resources
1401 * @mgr: Pointer to rotator manager
1402 */
1403static void sde_hw_rotator_destroy(struct sde_rot_mgr *mgr)
1404{
1405 struct sde_rot_data_type *mdata = sde_rot_get_mdata();
1406 struct sde_hw_rotator *rot;
1407
1408 if (!mgr || !mgr->pdev || !mgr->hw_data) {
1409 SDEROT_ERR("null parameters\n");
1410 return;
1411 }
1412
1413 rot = mgr->hw_data;
1414 if (rot->irq_num >= 0)
1415 devm_free_irq(&mgr->pdev->dev, rot->irq_num, mdata);
1416
1417 if (rot->mode == ROT_REGDMA_ON)
1418 sde_hw_rotator_swtc_destroy(rot);
1419
1420 devm_kfree(&mgr->pdev->dev, mgr->hw_data);
1421 mgr->hw_data = NULL;
1422}
1423
1424/*
1425 * sde_hw_rotator_alloc_ext - allocate rotator resource from rotator hw
1426 * @mgr: Pointer to rotator manager
1427 * @pipe_id: pipe identifier (not used)
1428 * @wb_id: writeback identifier/priority queue identifier
1429 *
1430 * This function allocates a new hw rotator resource for the given priority.
1431 */
1432static struct sde_rot_hw_resource *sde_hw_rotator_alloc_ext(
1433 struct sde_rot_mgr *mgr, u32 pipe_id, u32 wb_id)
1434{
1435 struct sde_hw_rotator_resource_info *resinfo;
1436
1437 if (!mgr || !mgr->hw_data) {
1438 SDEROT_ERR("null parameters\n");
1439 return NULL;
1440 }
1441
1442 /*
1443 * Allocate rotator resource info. Each allocation is per
1444 * HW priority queue
1445 */
1446 resinfo = devm_kzalloc(&mgr->pdev->dev, sizeof(*resinfo), GFP_KERNEL);
1447 if (!resinfo) {
1448 SDEROT_ERR("Failed allocation HW rotator resource info\n");
1449 return NULL;
1450 }
1451
1452 resinfo->rot = mgr->hw_data;
1453 resinfo->hw.wb_id = wb_id;
1454 atomic_set(&resinfo->hw.num_active, 0);
1455 init_waitqueue_head(&resinfo->hw.wait_queue);
1456
1457 /* For non-regdma, only support one active session */
1458 if (resinfo->rot->mode == ROT_REGDMA_OFF)
1459 resinfo->hw.max_active = 1;
1460 else {
1461 resinfo->hw.max_active = SDE_HW_ROT_REGDMA_TOTAL_CTX - 1;
1462
1463 if (resinfo->rot->iclient == NULL)
1464 sde_hw_rotator_swts_create(resinfo->rot);
1465 }
1466
Alan Kwongf987ea32016-07-06 12:11:44 -04001467 if (resinfo->rot->irq_num >= 0)
Alan Kwong818b7fc2016-07-24 22:07:41 -04001468 sde_hw_rotator_enable_irq(resinfo->rot);
Alan Kwongf987ea32016-07-06 12:11:44 -04001469
Alan Kwong9487de22016-01-16 22:06:36 -05001470 SDEROT_DBG("New rotator resource:%p, priority:%d\n",
1471 resinfo, wb_id);
1472
1473 return &resinfo->hw;
1474}
1475
1476/*
1477 * sde_hw_rotator_free_ext - free the given rotator resource
1478 * @mgr: Pointer to rotator manager
1479 * @hw: Pointer to rotator resource
1480 */
1481static void sde_hw_rotator_free_ext(struct sde_rot_mgr *mgr,
1482 struct sde_rot_hw_resource *hw)
1483{
1484 struct sde_hw_rotator_resource_info *resinfo;
1485
1486 if (!mgr || !mgr->hw_data)
1487 return;
1488
1489 resinfo = container_of(hw, struct sde_hw_rotator_resource_info, hw);
1490
1491 SDEROT_DBG(
1492 "Free rotator resource:%p, priority:%d, active:%d, pending:%d\n",
1493 resinfo, hw->wb_id, atomic_read(&hw->num_active),
1494 hw->pending_count);
1495
Alan Kwongf987ea32016-07-06 12:11:44 -04001496 if (resinfo->rot->irq_num >= 0)
Alan Kwong818b7fc2016-07-24 22:07:41 -04001497 sde_hw_rotator_disable_irq(resinfo->rot);
Alan Kwongf987ea32016-07-06 12:11:44 -04001498
Alan Kwong9487de22016-01-16 22:06:36 -05001499 devm_kfree(&mgr->pdev->dev, resinfo);
1500}
1501
1502/*
1503 * sde_hw_rotator_alloc_rotctx - allocate rotator context
1504 * @rot: Pointer to rotator hw
1505 * @hw: Pointer to rotator resource
1506 * @session_id: Session identifier of this context
1507 *
1508 * This function allocates a new rotator context for the given session id.
1509 */
1510static struct sde_hw_rotator_context *sde_hw_rotator_alloc_rotctx(
1511 struct sde_hw_rotator *rot,
1512 struct sde_rot_hw_resource *hw,
1513 u32 session_id)
1514{
1515 struct sde_hw_rotator_context *ctx;
1516
1517 /* Allocate rotator context */
1518 ctx = devm_kzalloc(&rot->pdev->dev, sizeof(*ctx), GFP_KERNEL);
1519 if (!ctx) {
1520 SDEROT_ERR("Failed allocation HW rotator context\n");
1521 return NULL;
1522 }
1523
1524 ctx->rot = rot;
1525 ctx->q_id = hw->wb_id;
1526 ctx->session_id = session_id;
1527 ctx->hwres = hw;
1528 ctx->timestamp = atomic_add_return(1, &rot->timestamp[ctx->q_id]);
1529 ctx->timestamp &= SDE_REGDMA_SWTS_MASK;
1530 ctx->is_secure = false;
1531
1532 ctx->regdma_base = rot->cmd_wr_ptr[ctx->q_id]
1533 [sde_hw_rotator_get_regdma_ctxidx(ctx)];
1534 ctx->regdma_wrptr = ctx->regdma_base;
1535 ctx->ts_addr = (dma_addr_t)((u32 *)rot->swts_buf.addr +
1536 ctx->q_id * SDE_HW_ROT_REGDMA_TOTAL_CTX +
1537 sde_hw_rotator_get_regdma_ctxidx(ctx));
1538
Alan Kwong818b7fc2016-07-24 22:07:41 -04001539 ctx->last_regdma_timestamp = SDE_REGDMA_SWTS_INVALID;
1540
Alan Kwong9487de22016-01-16 22:06:36 -05001541 init_completion(&ctx->rot_comp);
Alan Kwong818b7fc2016-07-24 22:07:41 -04001542 init_waitqueue_head(&ctx->regdma_waitq);
Alan Kwong9487de22016-01-16 22:06:36 -05001543
1544 /* Store rotator context for lookup purpose */
1545 sde_hw_rotator_put_ctx(ctx);
1546
1547 SDEROT_DBG(
1548 "New rot CTX:%p, ctxidx:%d, session-id:%d, prio:%d, timestamp:%X, active:%d\n",
1549 ctx, sde_hw_rotator_get_regdma_ctxidx(ctx), ctx->session_id,
1550 ctx->q_id, ctx->timestamp,
1551 atomic_read(&ctx->hwres->num_active));
1552
1553 return ctx;
1554}
1555
1556/*
1557 * sde_hw_rotator_free_rotctx - free the given rotator context
1558 * @rot: Pointer to rotator hw
1559 * @ctx: Pointer to rotator context
1560 */
1561static void sde_hw_rotator_free_rotctx(struct sde_hw_rotator *rot,
1562 struct sde_hw_rotator_context *ctx)
1563{
1564 if (!rot || !ctx)
1565 return;
1566
1567 SDEROT_DBG(
1568 "Free rot CTX:%p, ctxidx:%d, session-id:%d, prio:%d, timestamp:%X, active:%d\n",
1569 ctx, sde_hw_rotator_get_regdma_ctxidx(ctx), ctx->session_id,
1570 ctx->q_id, ctx->timestamp,
1571 atomic_read(&ctx->hwres->num_active));
1572
Benjamin Chanc3e185f2016-11-08 21:48:21 -05001573 /* Clear rotator context from lookup purpose */
1574 sde_hw_rotator_clr_ctx(ctx);
Alan Kwong9487de22016-01-16 22:06:36 -05001575
1576 devm_kfree(&rot->pdev->dev, ctx);
1577}
1578
1579/*
1580 * sde_hw_rotator_config - configure hw for the given rotation entry
1581 * @hw: Pointer to rotator resource
1582 * @entry: Pointer to rotation entry
1583 *
1584 * This function setup the fetch/writeback/rotator blocks, as well as VBIF
1585 * based on the given rotation entry.
1586 */
1587static int sde_hw_rotator_config(struct sde_rot_hw_resource *hw,
1588 struct sde_rot_entry *entry)
1589{
1590 struct sde_rot_data_type *mdata = sde_rot_get_mdata();
1591 struct sde_hw_rotator *rot;
1592 struct sde_hw_rotator_resource_info *resinfo;
1593 struct sde_hw_rotator_context *ctx;
1594 struct sde_hw_rot_sspp_cfg sspp_cfg;
1595 struct sde_hw_rot_wb_cfg wb_cfg;
1596 u32 danger_lut = 0; /* applicable for realtime client only */
1597 u32 safe_lut = 0; /* applicable for realtime client only */
1598 u32 flags = 0;
1599 struct sde_rotation_item *item;
1600
1601 if (!hw || !entry) {
1602 SDEROT_ERR("null hw resource/entry\n");
1603 return -EINVAL;
1604 }
1605
1606 resinfo = container_of(hw, struct sde_hw_rotator_resource_info, hw);
1607 rot = resinfo->rot;
1608 item = &entry->item;
1609
1610 ctx = sde_hw_rotator_alloc_rotctx(rot, hw, item->session_id);
1611 if (!ctx) {
1612 SDEROT_ERR("Failed allocating rotator context!!\n");
1613 return -EINVAL;
1614 }
1615
Benjamin Chan0f9e61d2016-09-16 16:01:09 -04001616 if (rot->reset_hw_ts) {
1617 SDEROT_EVTLOG(rot->last_hw_ts);
1618 SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_TIMESTAMP_REG,
1619 rot->last_hw_ts);
1620 /* ensure write is issued to the rotator HW */
1621 wmb();
1622 rot->reset_hw_ts = false;
1623 }
1624
Alan Kwong9487de22016-01-16 22:06:36 -05001625 flags = (item->flags & SDE_ROTATION_FLIP_LR) ?
1626 SDE_ROT_FLAG_FLIP_LR : 0;
1627 flags |= (item->flags & SDE_ROTATION_FLIP_UD) ?
1628 SDE_ROT_FLAG_FLIP_UD : 0;
1629 flags |= (item->flags & SDE_ROTATION_90) ?
1630 SDE_ROT_FLAG_ROT_90 : 0;
1631 flags |= (item->flags & SDE_ROTATION_DEINTERLACE) ?
1632 SDE_ROT_FLAG_DEINTERLACE : 0;
1633 flags |= (item->flags & SDE_ROTATION_SECURE) ?
1634 SDE_ROT_FLAG_SECURE_OVERLAY_SESSION : 0;
Abhijit Kulkarni298c8232016-09-26 22:32:10 -07001635 flags |= (item->flags & SDE_ROTATION_SECURE_CAMERA) ?
1636 SDE_ROT_FLAG_SECURE_CAMERA_SESSION : 0;
1637
Alan Kwong9487de22016-01-16 22:06:36 -05001638
1639 sspp_cfg.img_width = item->input.width;
1640 sspp_cfg.img_height = item->input.height;
Benjamin Chan99eb63b2016-12-21 15:45:26 -05001641 sspp_cfg.fps = entry->perf->config.frame_rate;
1642 sspp_cfg.bw = entry->perf->bw;
Alan Kwong9487de22016-01-16 22:06:36 -05001643 sspp_cfg.fmt = sde_get_format_params(item->input.format);
1644 if (!sspp_cfg.fmt) {
1645 SDEROT_ERR("null format\n");
1646 return -EINVAL;
1647 }
1648 sspp_cfg.src_rect = &item->src_rect;
1649 sspp_cfg.data = &entry->src_buf;
1650 sde_mdp_get_plane_sizes(sspp_cfg.fmt, item->input.width,
1651 item->input.height, &sspp_cfg.src_plane,
1652 0, /* No bwc_mode */
1653 (flags & SDE_ROT_FLAG_SOURCE_ROTATED_90) ?
1654 true : false);
1655
1656 rot->ops.setup_rotator_fetchengine(ctx, ctx->q_id,
Benjamin Chanfb6faa32016-08-16 17:21:01 -04001657 &sspp_cfg, danger_lut, safe_lut,
1658 entry->dnsc_factor_w, entry->dnsc_factor_h, flags);
Alan Kwong9487de22016-01-16 22:06:36 -05001659
1660 wb_cfg.img_width = item->output.width;
1661 wb_cfg.img_height = item->output.height;
Benjamin Chan99eb63b2016-12-21 15:45:26 -05001662 wb_cfg.fps = entry->perf->config.frame_rate;
1663 wb_cfg.bw = entry->perf->bw;
Alan Kwong9487de22016-01-16 22:06:36 -05001664 wb_cfg.fmt = sde_get_format_params(item->output.format);
1665 wb_cfg.dst_rect = &item->dst_rect;
1666 wb_cfg.data = &entry->dst_buf;
1667 sde_mdp_get_plane_sizes(wb_cfg.fmt, item->output.width,
1668 item->output.height, &wb_cfg.dst_plane,
1669 0, /* No bwc_mode */
1670 (flags & SDE_ROT_FLAG_ROT_90) ? true : false);
1671
1672 wb_cfg.v_downscale_factor = entry->dnsc_factor_h;
1673 wb_cfg.h_downscale_factor = entry->dnsc_factor_w;
1674
1675 rot->ops.setup_rotator_wbengine(ctx, ctx->q_id, &wb_cfg, flags);
1676
1677 /* setup VA mapping for debugfs */
1678 if (rot->dbgmem) {
1679 sde_hw_rotator_map_vaddr(&ctx->src_dbgbuf,
1680 &item->input,
1681 &entry->src_buf);
1682
1683 sde_hw_rotator_map_vaddr(&ctx->dst_dbgbuf,
1684 &item->output,
1685 &entry->dst_buf);
1686 }
1687
Benjamin Chan0f9e61d2016-09-16 16:01:09 -04001688 SDEROT_EVTLOG(ctx->timestamp, flags,
1689 item->input.width, item->input.height,
Benjamin Chan53e3bce2016-08-31 14:43:29 -04001690 item->output.width, item->output.height,
Benjamin Chan59a06052017-01-12 18:06:03 -05001691 entry->src_buf.p[0].addr, entry->dst_buf.p[0].addr,
1692 item->input.format, item->output.format);
Benjamin Chan53e3bce2016-08-31 14:43:29 -04001693
Alan Kwong9487de22016-01-16 22:06:36 -05001694 if (mdata->default_ot_rd_limit) {
1695 struct sde_mdp_set_ot_params ot_params;
1696
1697 memset(&ot_params, 0, sizeof(struct sde_mdp_set_ot_params));
1698 ot_params.xin_id = XIN_SSPP;
1699 ot_params.num = 0; /* not used */
Alan Kwongeffb5ee2016-03-12 19:47:45 -05001700 ot_params.width = entry->perf->config.input.width;
1701 ot_params.height = entry->perf->config.input.height;
1702 ot_params.fps = entry->perf->config.frame_rate;
Alan Kwong9487de22016-01-16 22:06:36 -05001703 ot_params.reg_off_vbif_lim_conf = MMSS_VBIF_RD_LIM_CONF;
1704 ot_params.reg_off_mdp_clk_ctrl =
1705 MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0;
1706 ot_params.bit_off_mdp_clk_ctrl =
1707 MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0_XIN0;
Benjamin Chan99eb63b2016-12-21 15:45:26 -05001708 ot_params.fmt = ctx->is_traffic_shaping ?
1709 SDE_PIX_FMT_ABGR_8888 :
1710 entry->perf->config.input.format;
Alan Kwong9487de22016-01-16 22:06:36 -05001711 sde_mdp_set_ot_limit(&ot_params);
1712 }
1713
1714 if (mdata->default_ot_wr_limit) {
1715 struct sde_mdp_set_ot_params ot_params;
1716
1717 memset(&ot_params, 0, sizeof(struct sde_mdp_set_ot_params));
1718 ot_params.xin_id = XIN_WRITEBACK;
1719 ot_params.num = 0; /* not used */
Alan Kwongeffb5ee2016-03-12 19:47:45 -05001720 ot_params.width = entry->perf->config.input.width;
1721 ot_params.height = entry->perf->config.input.height;
1722 ot_params.fps = entry->perf->config.frame_rate;
Alan Kwong9487de22016-01-16 22:06:36 -05001723 ot_params.reg_off_vbif_lim_conf = MMSS_VBIF_WR_LIM_CONF;
1724 ot_params.reg_off_mdp_clk_ctrl =
1725 MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0;
1726 ot_params.bit_off_mdp_clk_ctrl =
1727 MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0_XIN1;
Benjamin Chan99eb63b2016-12-21 15:45:26 -05001728 ot_params.fmt = ctx->is_traffic_shaping ?
1729 SDE_PIX_FMT_ABGR_8888 :
1730 entry->perf->config.input.format;
Alan Kwong9487de22016-01-16 22:06:36 -05001731 sde_mdp_set_ot_limit(&ot_params);
1732 }
1733
1734 if (test_bit(SDE_QOS_PER_PIPE_LUT, mdata->sde_qos_map)) {
1735 u32 qos_lut = 0; /* low priority for nrt read client */
1736
1737 trace_rot_perf_set_qos_luts(XIN_SSPP, sspp_cfg.fmt->format,
1738 qos_lut, sde_mdp_is_linear_format(sspp_cfg.fmt));
1739
1740 SDE_ROTREG_WRITE(rot->mdss_base, ROT_SSPP_CREQ_LUT, qos_lut);
1741 }
1742
1743 if (mdata->npriority_lvl > 0) {
1744 u32 mask, reg_val, i, vbif_qos;
1745
1746 for (i = 0; i < mdata->npriority_lvl; i++) {
1747 reg_val = SDE_VBIF_READ(mdata,
1748 MMSS_VBIF_NRT_VBIF_QOS_REMAP_00 + i*4);
1749 mask = 0x3 << (XIN_SSPP * 2);
1750 reg_val &= ~(mask);
1751 vbif_qos = mdata->vbif_nrt_qos[i];
1752 reg_val |= vbif_qos << (XIN_SSPP * 2);
1753 /* ensure write is issued after the read operation */
1754 mb();
1755 SDE_VBIF_WRITE(mdata,
1756 MMSS_VBIF_NRT_VBIF_QOS_REMAP_00 + i*4,
1757 reg_val);
1758 }
1759 }
1760
1761 /* Enable write gather for writeback to remove write gaps, which
1762 * may hang AXI/BIMC/SDE.
1763 */
1764 SDE_VBIF_WRITE(mdata, MMSS_VBIF_NRT_VBIF_WRITE_GATHTER_EN,
1765 BIT(XIN_WRITEBACK));
1766
1767 return 0;
1768}
1769
1770/*
1771 * sde_hw_rotator_kickoff - kickoff processing on the given entry
1772 * @hw: Pointer to rotator resource
1773 * @entry: Pointer to rotation entry
1774 */
1775static int sde_hw_rotator_kickoff(struct sde_rot_hw_resource *hw,
1776 struct sde_rot_entry *entry)
1777{
1778 struct sde_hw_rotator *rot;
1779 struct sde_hw_rotator_resource_info *resinfo;
1780 struct sde_hw_rotator_context *ctx;
Alan Kwong9487de22016-01-16 22:06:36 -05001781
1782 if (!hw || !entry) {
1783 SDEROT_ERR("null hw resource/entry\n");
1784 return -EINVAL;
1785 }
1786
1787 resinfo = container_of(hw, struct sde_hw_rotator_resource_info, hw);
1788 rot = resinfo->rot;
1789
1790 /* Lookup rotator context from session-id */
1791 ctx = sde_hw_rotator_get_ctx(rot, entry->item.session_id, hw->wb_id);
1792 if (!ctx) {
1793 SDEROT_ERR("Cannot locate rotator ctx from sesison id:%d\n",
1794 entry->item.session_id);
Benjamin Chan62b94ed2016-08-18 23:55:21 -04001795 return -EINVAL;
Alan Kwong9487de22016-01-16 22:06:36 -05001796 }
Alan Kwong9487de22016-01-16 22:06:36 -05001797
Alan Kwong9487de22016-01-16 22:06:36 -05001798 rot->ops.start_rotator(ctx, ctx->q_id);
1799
1800 return 0;
1801}
1802
1803/*
1804 * sde_hw_rotator_wait4done - wait for completion notification
1805 * @hw: Pointer to rotator resource
1806 * @entry: Pointer to rotation entry
1807 *
1808 * This function blocks until the given entry is complete, error
1809 * is detected, or timeout.
1810 */
1811static int sde_hw_rotator_wait4done(struct sde_rot_hw_resource *hw,
1812 struct sde_rot_entry *entry)
1813{
1814 struct sde_hw_rotator *rot;
1815 struct sde_hw_rotator_resource_info *resinfo;
1816 struct sde_hw_rotator_context *ctx;
1817 int ret;
1818
1819 if (!hw || !entry) {
1820 SDEROT_ERR("null hw resource/entry\n");
1821 return -EINVAL;
1822 }
1823
1824 resinfo = container_of(hw, struct sde_hw_rotator_resource_info, hw);
1825 rot = resinfo->rot;
1826
1827 /* Lookup rotator context from session-id */
1828 ctx = sde_hw_rotator_get_ctx(rot, entry->item.session_id, hw->wb_id);
1829 if (!ctx) {
1830 SDEROT_ERR("Cannot locate rotator ctx from sesison id:%d\n",
1831 entry->item.session_id);
Benjamin Chan62b94ed2016-08-18 23:55:21 -04001832 return -EINVAL;
Alan Kwong9487de22016-01-16 22:06:36 -05001833 }
Alan Kwong9487de22016-01-16 22:06:36 -05001834
1835 ret = rot->ops.wait_rotator_done(ctx, ctx->q_id, 0);
1836
Alan Kwong9487de22016-01-16 22:06:36 -05001837 if (rot->dbgmem) {
1838 sde_hw_rotator_unmap_vaddr(&ctx->src_dbgbuf);
1839 sde_hw_rotator_unmap_vaddr(&ctx->dst_dbgbuf);
1840 }
1841
1842 /* Current rotator context job is finished, time to free up*/
1843 sde_hw_rotator_free_rotctx(rot, ctx);
1844
1845 return ret;
1846}
1847
1848/*
1849 * sde_rotator_hw_rev_init - setup feature and/or capability bitmask
1850 * @rot: Pointer to hw rotator
1851 *
1852 * This function initializes feature and/or capability bitmask based on
1853 * h/w version read from the device.
1854 */
1855static int sde_rotator_hw_rev_init(struct sde_hw_rotator *rot)
1856{
1857 struct sde_rot_data_type *mdata = sde_rot_get_mdata();
1858 u32 hw_version;
1859
1860 if (!mdata) {
1861 SDEROT_ERR("null rotator data\n");
1862 return -EINVAL;
1863 }
1864
1865 hw_version = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_HW_VERSION);
1866 SDEROT_DBG("hw version %8.8x\n", hw_version);
1867
1868 clear_bit(SDE_QOS_PER_PIPE_IB, mdata->sde_qos_map);
1869 set_bit(SDE_QOS_OVERHEAD_FACTOR, mdata->sde_qos_map);
1870 clear_bit(SDE_QOS_CDP, mdata->sde_qos_map);
1871 set_bit(SDE_QOS_OTLIM, mdata->sde_qos_map);
1872 set_bit(SDE_QOS_PER_PIPE_LUT, mdata->sde_qos_map);
1873 clear_bit(SDE_QOS_SIMPLIFIED_PREFILL, mdata->sde_qos_map);
1874
1875 set_bit(SDE_CAPS_R3_WB, mdata->sde_caps_map);
1876
Benjamin Chanfb6faa32016-08-16 17:21:01 -04001877 if (hw_version != SDE_ROT_TYPE_V1_0) {
1878 SDEROT_DBG("Supporting 1.5 downscale for SDE Rotator\n");
1879 set_bit(SDE_CAPS_R3_1P5_DOWNSCALE, mdata->sde_caps_map);
1880 }
1881
Abhijit Kulkarni298c8232016-09-26 22:32:10 -07001882 set_bit(SDE_CAPS_SEC_ATTACH_DETACH_SMMU, mdata->sde_caps_map);
1883
Benjamin Chan53e3bce2016-08-31 14:43:29 -04001884 mdata->nrt_vbif_dbg_bus = nrt_vbif_dbg_bus_r3;
1885 mdata->nrt_vbif_dbg_bus_size =
1886 ARRAY_SIZE(nrt_vbif_dbg_bus_r3);
1887
1888 mdata->regdump = sde_rot_r3_regdump;
1889 mdata->regdump_size = ARRAY_SIZE(sde_rot_r3_regdump);
Benjamin Chan0f9e61d2016-09-16 16:01:09 -04001890 SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_TIMESTAMP_REG, 0);
Alan Kwong9487de22016-01-16 22:06:36 -05001891 return 0;
1892}
1893
1894/*
1895 * sde_hw_rotator_rotirq_handler - non-regdma interrupt handler
1896 * @irq: Interrupt number
1897 * @ptr: Pointer to private handle provided during registration
1898 *
1899 * This function services rotator interrupt and wakes up waiting client
1900 * with pending rotation requests already submitted to h/w.
1901 */
1902static irqreturn_t sde_hw_rotator_rotirq_handler(int irq, void *ptr)
1903{
1904 struct sde_hw_rotator *rot = ptr;
1905 struct sde_hw_rotator_context *ctx;
1906 irqreturn_t ret = IRQ_NONE;
1907 u32 isr;
1908
1909 isr = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_INTR_STATUS);
1910
1911 SDEROT_DBG("intr_status = %8.8x\n", isr);
1912
1913 if (isr & ROT_DONE_MASK) {
1914 if (rot->irq_num >= 0)
Alan Kwong818b7fc2016-07-24 22:07:41 -04001915 sde_hw_rotator_disable_irq(rot);
Alan Kwong9487de22016-01-16 22:06:36 -05001916 SDEROT_DBG("Notify rotator complete\n");
1917
1918 /* Normal rotator only 1 session, no need to lookup */
1919 ctx = rot->rotCtx[0][0];
1920 WARN_ON(ctx == NULL);
1921 complete_all(&ctx->rot_comp);
1922
1923 spin_lock(&rot->rotisr_lock);
1924 SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_CLEAR,
1925 ROT_DONE_CLEAR);
1926 spin_unlock(&rot->rotisr_lock);
1927 ret = IRQ_HANDLED;
1928 }
1929
1930 return ret;
1931}
1932
1933/*
1934 * sde_hw_rotator_regdmairq_handler - regdma interrupt handler
1935 * @irq: Interrupt number
1936 * @ptr: Pointer to private handle provided during registration
1937 *
1938 * This function services rotator interrupt, decoding the source of
1939 * events (high/low priority queue), and wakes up all waiting clients
1940 * with pending rotation requests already submitted to h/w.
1941 */
1942static irqreturn_t sde_hw_rotator_regdmairq_handler(int irq, void *ptr)
1943{
1944 struct sde_hw_rotator *rot = ptr;
1945 struct sde_hw_rotator_context *ctx;
1946 irqreturn_t ret = IRQ_NONE;
1947 u32 isr;
1948 u32 ts;
1949 u32 q_id;
1950
1951 isr = SDE_ROTREG_READ(rot->mdss_base, REGDMA_CSR_REGDMA_INT_STATUS);
Alan Kwong818b7fc2016-07-24 22:07:41 -04001952 /* acknowledge interrupt before reading latest timestamp */
1953 SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_CLEAR, isr);
Alan Kwong9487de22016-01-16 22:06:36 -05001954 ts = SDE_ROTREG_READ(rot->mdss_base, REGDMA_TIMESTAMP_REG);
1955
1956 SDEROT_DBG("intr_status = %8.8x, sw_TS:%X\n", isr, ts);
1957
1958 /* Any REGDMA status, including error and watchdog timer, should
1959 * trigger and wake up waiting thread
1960 */
1961 if (isr & (REGDMA_INT_HIGH_MASK | REGDMA_INT_LOW_MASK)) {
1962 spin_lock(&rot->rotisr_lock);
1963
1964 /*
1965 * Obtain rotator context based on timestamp from regdma
1966 * and low/high interrupt status
1967 */
1968 if (isr & REGDMA_INT_HIGH_MASK) {
1969 q_id = ROT_QUEUE_HIGH_PRIORITY;
1970 ts = ts & SDE_REGDMA_SWTS_MASK;
1971 } else if (isr & REGDMA_INT_LOW_MASK) {
1972 q_id = ROT_QUEUE_LOW_PRIORITY;
1973 ts = (ts >> SDE_REGDMA_SWTS_SHIFT) &
1974 SDE_REGDMA_SWTS_MASK;
Benjamin Chan62b94ed2016-08-18 23:55:21 -04001975 } else {
1976 SDEROT_ERR("unknown ISR status: isr=0x%X\n", isr);
1977 goto done_isr_handle;
Alan Kwong9487de22016-01-16 22:06:36 -05001978 }
Alan Kwong9487de22016-01-16 22:06:36 -05001979 ctx = rot->rotCtx[q_id][ts & SDE_HW_ROT_REGDMA_SEG_MASK];
Alan Kwong9487de22016-01-16 22:06:36 -05001980
1981 /*
1982 * Wake up all waiting context from the current and previous
1983 * SW Timestamp.
1984 */
Alan Kwong818b7fc2016-07-24 22:07:41 -04001985 while (ctx &&
1986 sde_hw_rotator_elapsed_swts(ctx->timestamp, ts) >= 0) {
Alan Kwong9487de22016-01-16 22:06:36 -05001987 ctx->last_regdma_isr_status = isr;
1988 ctx->last_regdma_timestamp = ts;
1989 SDEROT_DBG(
Alan Kwongf987ea32016-07-06 12:11:44 -04001990 "regdma complete: ctx:%p, ts:%X\n", ctx, ts);
Alan Kwong818b7fc2016-07-24 22:07:41 -04001991 wake_up_all(&ctx->regdma_waitq);
Alan Kwong9487de22016-01-16 22:06:36 -05001992
1993 ts = (ts - 1) & SDE_REGDMA_SWTS_MASK;
1994 ctx = rot->rotCtx[q_id]
1995 [ts & SDE_HW_ROT_REGDMA_SEG_MASK];
Alan Kwong818b7fc2016-07-24 22:07:41 -04001996 };
Alan Kwong9487de22016-01-16 22:06:36 -05001997
Benjamin Chan62b94ed2016-08-18 23:55:21 -04001998done_isr_handle:
Alan Kwong9487de22016-01-16 22:06:36 -05001999 spin_unlock(&rot->rotisr_lock);
2000 ret = IRQ_HANDLED;
2001 } else if (isr & REGDMA_INT_ERR_MASK) {
2002 /*
2003 * For REGDMA Err, we save the isr info and wake up
2004 * all waiting contexts
2005 */
2006 int i, j;
2007
2008 SDEROT_ERR(
2009 "regdma err isr:%X, wake up all waiting contexts\n",
2010 isr);
2011
2012 spin_lock(&rot->rotisr_lock);
2013
2014 for (i = 0; i < ROT_QUEUE_MAX; i++) {
2015 for (j = 0; j < SDE_HW_ROT_REGDMA_TOTAL_CTX; j++) {
2016 ctx = rot->rotCtx[i][j];
2017 if (ctx && ctx->last_regdma_isr_status == 0) {
2018 ctx->last_regdma_isr_status = isr;
2019 ctx->last_regdma_timestamp = ts;
Alan Kwong818b7fc2016-07-24 22:07:41 -04002020 wake_up_all(&ctx->regdma_waitq);
Alan Kwong9487de22016-01-16 22:06:36 -05002021 SDEROT_DBG("Wakeup rotctx[%d][%d]:%p\n",
2022 i, j, ctx);
2023 }
2024 }
2025 }
2026
Alan Kwong9487de22016-01-16 22:06:36 -05002027 spin_unlock(&rot->rotisr_lock);
2028 ret = IRQ_HANDLED;
2029 }
2030
2031 return ret;
2032}
2033
2034/*
2035 * sde_hw_rotator_validate_entry - validate rotation entry
2036 * @mgr: Pointer to rotator manager
2037 * @entry: Pointer to rotation entry
2038 *
2039 * This function validates the given rotation entry and provides possible
2040 * fixup (future improvement) if available. This function returns 0 if
2041 * the entry is valid, and returns error code otherwise.
2042 */
2043static int sde_hw_rotator_validate_entry(struct sde_rot_mgr *mgr,
2044 struct sde_rot_entry *entry)
2045{
Benjamin Chanfb6faa32016-08-16 17:21:01 -04002046 struct sde_rot_data_type *mdata = sde_rot_get_mdata();
Alan Kwong9487de22016-01-16 22:06:36 -05002047 int ret = 0;
2048 u16 src_w, src_h, dst_w, dst_h;
2049 struct sde_rotation_item *item = &entry->item;
2050 struct sde_mdp_format_params *fmt;
2051
2052 src_w = item->src_rect.w;
2053 src_h = item->src_rect.h;
2054
2055 if (item->flags & SDE_ROTATION_90) {
2056 dst_w = item->dst_rect.h;
2057 dst_h = item->dst_rect.w;
2058 } else {
2059 dst_w = item->dst_rect.w;
2060 dst_h = item->dst_rect.h;
2061 }
2062
2063 entry->dnsc_factor_w = 0;
2064 entry->dnsc_factor_h = 0;
2065
2066 if ((src_w != dst_w) || (src_h != dst_h)) {
2067 if ((src_w % dst_w) || (src_h % dst_h)) {
2068 SDEROT_DBG("non integral scale not support\n");
2069 ret = -EINVAL;
Benjamin Chanfb6faa32016-08-16 17:21:01 -04002070 goto dnsc_1p5_check;
Alan Kwong9487de22016-01-16 22:06:36 -05002071 }
2072 entry->dnsc_factor_w = src_w / dst_w;
2073 if ((entry->dnsc_factor_w & (entry->dnsc_factor_w - 1)) ||
2074 (entry->dnsc_factor_w > 64)) {
2075 SDEROT_DBG("non power-of-2 w_scale not support\n");
2076 ret = -EINVAL;
2077 goto dnsc_err;
2078 }
2079 entry->dnsc_factor_h = src_h / dst_h;
2080 if ((entry->dnsc_factor_h & (entry->dnsc_factor_h - 1)) ||
2081 (entry->dnsc_factor_h > 64)) {
2082 SDEROT_DBG("non power-of-2 h_scale not support\n");
2083 ret = -EINVAL;
2084 goto dnsc_err;
2085 }
2086 }
2087
Benjamin Chan886ff672016-11-07 15:23:17 -05002088 fmt = sde_get_format_params(item->input.format);
2089 /*
2090 * Rotator downscale support max 4 times for UBWC format and
2091 * max 2 times for TP10/TP10_UBWC format
2092 */
2093 if (sde_mdp_is_ubwc_format(fmt) && (entry->dnsc_factor_h > 4)) {
2094 SDEROT_DBG("max downscale for UBWC format is 4\n");
Alan Kwong9487de22016-01-16 22:06:36 -05002095 ret = -EINVAL;
2096 goto dnsc_err;
2097 }
Benjamin Chan886ff672016-11-07 15:23:17 -05002098 if (sde_mdp_is_tp10_format(fmt) && (entry->dnsc_factor_h > 2)) {
2099 SDEROT_DBG("downscale with TP10 cannot be more than 2\n");
Alan Kwong9487de22016-01-16 22:06:36 -05002100 ret = -EINVAL;
2101 }
Benjamin Chanfb6faa32016-08-16 17:21:01 -04002102 goto dnsc_err;
2103
2104dnsc_1p5_check:
2105 /* Check for 1.5 downscale that only applies to V2 HW */
2106 if (test_bit(SDE_CAPS_R3_1P5_DOWNSCALE, mdata->sde_caps_map)) {
2107 entry->dnsc_factor_w = src_w / dst_w;
2108 if ((entry->dnsc_factor_w != 1) ||
2109 ((dst_w * 3) != (src_w * 2))) {
2110 SDEROT_DBG(
2111 "No supporting non 1.5 downscale width ratio, src_w:%d, dst_w:%d\n",
2112 src_w, dst_w);
2113 ret = -EINVAL;
2114 goto dnsc_err;
2115 }
2116
2117 entry->dnsc_factor_h = src_h / dst_h;
2118 if ((entry->dnsc_factor_h != 1) ||
2119 ((dst_h * 3) != (src_h * 2))) {
2120 SDEROT_DBG(
2121 "Not supporting non 1.5 downscale height ratio, src_h:%d, dst_h:%d\n",
2122 src_h, dst_h);
2123 ret = -EINVAL;
2124 goto dnsc_err;
2125 }
2126 ret = 0;
2127 }
Alan Kwong9487de22016-01-16 22:06:36 -05002128
2129dnsc_err:
2130 /* Downscaler does not support asymmetrical dnsc */
2131 if (entry->dnsc_factor_w != entry->dnsc_factor_h) {
2132 SDEROT_DBG("asymmetric downscale not support\n");
2133 ret = -EINVAL;
2134 }
2135
2136 if (ret) {
2137 entry->dnsc_factor_w = 0;
2138 entry->dnsc_factor_h = 0;
2139 }
2140 return ret;
2141}
2142
2143/*
2144 * sde_hw_rotator_show_caps - output capability info to sysfs 'caps' file
2145 * @mgr: Pointer to rotator manager
2146 * @attr: Pointer to device attribute interface
2147 * @buf: Pointer to output buffer
2148 * @len: Length of output buffer
2149 */
2150static ssize_t sde_hw_rotator_show_caps(struct sde_rot_mgr *mgr,
2151 struct device_attribute *attr, char *buf, ssize_t len)
2152{
2153 struct sde_hw_rotator *hw_data;
Benjamin Chan886ff672016-11-07 15:23:17 -05002154 struct sde_rot_data_type *mdata = sde_rot_get_mdata();
Alan Kwong9487de22016-01-16 22:06:36 -05002155 int cnt = 0;
2156
2157 if (!mgr || !buf)
2158 return 0;
2159
2160 hw_data = mgr->hw_data;
2161
2162#define SPRINT(fmt, ...) \
2163 (cnt += scnprintf(buf + cnt, len - cnt, fmt, ##__VA_ARGS__))
2164
2165 /* insert capabilities here */
Benjamin Chan886ff672016-11-07 15:23:17 -05002166 if (test_bit(SDE_CAPS_R3_1P5_DOWNSCALE, mdata->sde_caps_map))
2167 SPRINT("min_downscale=1.5\n");
2168 else
2169 SPRINT("min_downscale=2.0\n");
Alan Kwong9487de22016-01-16 22:06:36 -05002170
Benjamin Chan42db2c92016-11-22 22:50:01 -05002171 SPRINT("downscale_compression=1\n");
2172
Alan Kwong9487de22016-01-16 22:06:36 -05002173#undef SPRINT
2174 return cnt;
2175}
2176
2177/*
2178 * sde_hw_rotator_show_state - output state info to sysfs 'state' file
2179 * @mgr: Pointer to rotator manager
2180 * @attr: Pointer to device attribute interface
2181 * @buf: Pointer to output buffer
2182 * @len: Length of output buffer
2183 */
2184static ssize_t sde_hw_rotator_show_state(struct sde_rot_mgr *mgr,
2185 struct device_attribute *attr, char *buf, ssize_t len)
2186{
2187 struct sde_hw_rotator *rot;
2188 struct sde_hw_rotator_context *ctx;
2189 int cnt = 0;
2190 int num_active = 0;
2191 int i, j;
2192
2193 if (!mgr || !buf) {
2194 SDEROT_ERR("null parameters\n");
2195 return 0;
2196 }
2197
2198 rot = mgr->hw_data;
2199
2200#define SPRINT(fmt, ...) \
2201 (cnt += scnprintf(buf + cnt, len - cnt, fmt, ##__VA_ARGS__))
2202
2203 if (rot) {
2204 SPRINT("rot_mode=%d\n", rot->mode);
2205 SPRINT("irq_num=%d\n", rot->irq_num);
2206
2207 if (rot->mode == ROT_REGDMA_OFF) {
2208 SPRINT("max_active=1\n");
2209 SPRINT("num_active=%d\n", rot->rotCtx[0][0] ? 1 : 0);
2210 } else {
2211 for (i = 0; i < ROT_QUEUE_MAX; i++) {
2212 for (j = 0; j < SDE_HW_ROT_REGDMA_TOTAL_CTX;
2213 j++) {
2214 ctx = rot->rotCtx[i][j];
2215
2216 if (ctx) {
2217 SPRINT(
2218 "rotCtx[%d][%d]:%p\n",
2219 i, j, ctx);
2220 ++num_active;
2221 }
2222 }
2223 }
2224
2225 SPRINT("max_active=%d\n", SDE_HW_ROT_REGDMA_TOTAL_CTX);
2226 SPRINT("num_active=%d\n", num_active);
2227 }
2228 }
2229
2230#undef SPRINT
2231 return cnt;
2232}
2233
2234/*
Alan Kwongda16e442016-08-14 20:47:18 -04002235 * sde_hw_rotator_get_pixfmt - get the indexed pixel format
2236 * @mgr: Pointer to rotator manager
2237 * @index: index of pixel format
2238 * @input: true for input port; false for output port
2239 */
2240static u32 sde_hw_rotator_get_pixfmt(struct sde_rot_mgr *mgr,
2241 int index, bool input)
2242{
2243 if (input) {
2244 if (index < ARRAY_SIZE(sde_hw_rotator_input_pixfmts))
2245 return sde_hw_rotator_input_pixfmts[index];
2246 else
2247 return 0;
2248 } else {
2249 if (index < ARRAY_SIZE(sde_hw_rotator_output_pixfmts))
2250 return sde_hw_rotator_output_pixfmts[index];
2251 else
2252 return 0;
2253 }
2254}
2255
2256/*
2257 * sde_hw_rotator_is_valid_pixfmt - verify if the given pixel format is valid
2258 * @mgr: Pointer to rotator manager
2259 * @pixfmt: pixel format to be verified
2260 * @input: true for input port; false for output port
2261 */
2262static int sde_hw_rotator_is_valid_pixfmt(struct sde_rot_mgr *mgr, u32 pixfmt,
2263 bool input)
2264{
2265 int i;
2266
2267 if (input) {
2268 for (i = 0; i < ARRAY_SIZE(sde_hw_rotator_input_pixfmts); i++)
2269 if (sde_hw_rotator_input_pixfmts[i] == pixfmt)
2270 return true;
2271 } else {
2272 for (i = 0; i < ARRAY_SIZE(sde_hw_rotator_output_pixfmts); i++)
2273 if (sde_hw_rotator_output_pixfmts[i] == pixfmt)
2274 return true;
2275 }
2276
2277 return false;
2278}
2279
2280/*
Alan Kwong9487de22016-01-16 22:06:36 -05002281 * sde_hw_rotator_parse_dt - parse r3 specific device tree settings
2282 * @hw_data: Pointer to rotator hw
2283 * @dev: Pointer to platform device
2284 */
2285static int sde_hw_rotator_parse_dt(struct sde_hw_rotator *hw_data,
2286 struct platform_device *dev)
2287{
2288 int ret = 0;
2289 u32 data;
2290
2291 if (!hw_data || !dev)
2292 return -EINVAL;
2293
2294 ret = of_property_read_u32(dev->dev.of_node, "qcom,mdss-rot-mode",
2295 &data);
2296 if (ret) {
2297 SDEROT_DBG("default to regdma off\n");
2298 ret = 0;
2299 hw_data->mode = ROT_REGDMA_OFF;
2300 } else if (data < ROT_REGDMA_MAX) {
2301 SDEROT_DBG("set to regdma mode %d\n", data);
2302 hw_data->mode = data;
2303 } else {
2304 SDEROT_ERR("regdma mode out of range. default to regdma off\n");
2305 hw_data->mode = ROT_REGDMA_OFF;
2306 }
2307
2308 ret = of_property_read_u32(dev->dev.of_node,
2309 "qcom,mdss-highest-bank-bit", &data);
2310 if (ret) {
2311 SDEROT_DBG("default to A5X bank\n");
2312 ret = 0;
2313 hw_data->highest_bank = 2;
2314 } else {
2315 SDEROT_DBG("set highest bank bit to %d\n", data);
2316 hw_data->highest_bank = data;
2317 }
2318
2319 return ret;
2320}
2321
2322/*
2323 * sde_rotator_r3_init - initialize the r3 module
2324 * @mgr: Pointer to rotator manager
2325 *
2326 * This function setup r3 callback functions, parses r3 specific
2327 * device tree settings, installs r3 specific interrupt handler,
2328 * as well as initializes r3 internal data structure.
2329 */
2330int sde_rotator_r3_init(struct sde_rot_mgr *mgr)
2331{
2332 struct sde_hw_rotator *rot;
2333 struct sde_rot_data_type *mdata = sde_rot_get_mdata();
2334 int i;
2335 int ret;
2336
2337 rot = devm_kzalloc(&mgr->pdev->dev, sizeof(*rot), GFP_KERNEL);
2338 if (!rot)
2339 return -ENOMEM;
2340
2341 mgr->hw_data = rot;
2342 mgr->queue_count = ROT_QUEUE_MAX;
2343
2344 rot->mdss_base = mdata->sde_io.base;
2345 rot->pdev = mgr->pdev;
2346
2347 /* Assign ops */
2348 mgr->ops_hw_destroy = sde_hw_rotator_destroy;
2349 mgr->ops_hw_alloc = sde_hw_rotator_alloc_ext;
2350 mgr->ops_hw_free = sde_hw_rotator_free_ext;
2351 mgr->ops_config_hw = sde_hw_rotator_config;
2352 mgr->ops_kickoff_entry = sde_hw_rotator_kickoff;
2353 mgr->ops_wait_for_entry = sde_hw_rotator_wait4done;
2354 mgr->ops_hw_validate_entry = sde_hw_rotator_validate_entry;
2355 mgr->ops_hw_show_caps = sde_hw_rotator_show_caps;
2356 mgr->ops_hw_show_state = sde_hw_rotator_show_state;
2357 mgr->ops_hw_create_debugfs = sde_rotator_r3_create_debugfs;
Alan Kwongda16e442016-08-14 20:47:18 -04002358 mgr->ops_hw_get_pixfmt = sde_hw_rotator_get_pixfmt;
2359 mgr->ops_hw_is_valid_pixfmt = sde_hw_rotator_is_valid_pixfmt;
Benjamin Chan0f9e61d2016-09-16 16:01:09 -04002360 mgr->ops_hw_pre_pmevent = sde_hw_rotator_pre_pmevent;
2361 mgr->ops_hw_post_pmevent = sde_hw_rotator_post_pmevent;
Alan Kwong9487de22016-01-16 22:06:36 -05002362
2363 ret = sde_hw_rotator_parse_dt(mgr->hw_data, mgr->pdev);
2364 if (ret)
2365 goto error_parse_dt;
2366
2367 rot->irq_num = platform_get_irq(mgr->pdev, 0);
2368 if (rot->irq_num < 0) {
2369 SDEROT_ERR("fail to get rotator irq\n");
2370 } else {
2371 if (rot->mode == ROT_REGDMA_OFF)
2372 ret = devm_request_threaded_irq(&mgr->pdev->dev,
2373 rot->irq_num,
2374 sde_hw_rotator_rotirq_handler,
2375 NULL, 0, "sde_rotator_r3", rot);
2376 else
2377 ret = devm_request_threaded_irq(&mgr->pdev->dev,
2378 rot->irq_num,
2379 sde_hw_rotator_regdmairq_handler,
2380 NULL, 0, "sde_rotator_r3", rot);
2381 if (ret) {
2382 SDEROT_ERR("fail to request irq r:%d\n", ret);
2383 rot->irq_num = -1;
2384 } else {
2385 disable_irq(rot->irq_num);
2386 }
2387 }
Alan Kwong818b7fc2016-07-24 22:07:41 -04002388 atomic_set(&rot->irq_enabled, 0);
Alan Kwong9487de22016-01-16 22:06:36 -05002389
2390 setup_rotator_ops(&rot->ops, rot->mode);
2391
2392 spin_lock_init(&rot->rotctx_lock);
2393 spin_lock_init(&rot->rotisr_lock);
2394
2395 /* REGDMA initialization */
2396 if (rot->mode == ROT_REGDMA_OFF) {
2397 for (i = 0; i < SDE_HW_ROT_REGDMA_TOTAL_CTX; i++)
2398 rot->cmd_wr_ptr[0][i] = &rot->cmd_queue[
2399 SDE_HW_ROT_REGDMA_SEG_SIZE * i];
2400 } else {
2401 for (i = 0; i < SDE_HW_ROT_REGDMA_TOTAL_CTX; i++)
2402 rot->cmd_wr_ptr[ROT_QUEUE_HIGH_PRIORITY][i] =
2403 (u32 *)(rot->mdss_base +
2404 REGDMA_RAM_REGDMA_CMD_RAM +
2405 SDE_HW_ROT_REGDMA_SEG_SIZE * 4 * i);
2406
2407 for (i = 0; i < SDE_HW_ROT_REGDMA_TOTAL_CTX; i++)
2408 rot->cmd_wr_ptr[ROT_QUEUE_LOW_PRIORITY][i] =
2409 (u32 *)(rot->mdss_base +
2410 REGDMA_RAM_REGDMA_CMD_RAM +
2411 SDE_HW_ROT_REGDMA_SEG_SIZE * 4 *
2412 (i + SDE_HW_ROT_REGDMA_TOTAL_CTX));
2413 }
2414
2415 atomic_set(&rot->timestamp[0], 0);
2416 atomic_set(&rot->timestamp[1], 0);
Alan Kwong9487de22016-01-16 22:06:36 -05002417
2418 ret = sde_rotator_hw_rev_init(rot);
2419 if (ret)
2420 goto error_hw_rev_init;
2421
Alan Kwong315cd772016-08-03 22:29:42 -04002422 /* set rotator CBCR to shutoff memory/periphery on clock off.*/
Benjamin Chan77aed192016-10-17 17:49:41 -04002423 clk_set_flags(mgr->rot_clk[SDE_ROTATOR_CLK_ROT_CORE].clk,
Alan Kwong315cd772016-08-03 22:29:42 -04002424 CLKFLAG_NORETAIN_MEM);
Benjamin Chan77aed192016-10-17 17:49:41 -04002425 clk_set_flags(mgr->rot_clk[SDE_ROTATOR_CLK_ROT_CORE].clk,
Alan Kwong315cd772016-08-03 22:29:42 -04002426 CLKFLAG_NORETAIN_PERIPH);
2427
Benjamin Chan53e3bce2016-08-31 14:43:29 -04002428 mdata->sde_rot_hw = rot;
Alan Kwong9487de22016-01-16 22:06:36 -05002429 return 0;
2430error_hw_rev_init:
2431 if (rot->irq_num >= 0)
2432 devm_free_irq(&mgr->pdev->dev, rot->irq_num, mdata);
2433 devm_kfree(&mgr->pdev->dev, mgr->hw_data);
2434error_parse_dt:
2435 return ret;
2436}