blob: 448d5d294308a8903956864385b2f6a77adfaf09 [file] [log] [blame]
Ken Zhangfebeabd2012-12-17 10:35:15 -05001/* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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#include <linux/platform_device.h>
14#include <linux/cdev.h>
15#include <linux/list.h>
16#include <linux/module.h>
17#include <linux/fs.h>
18#include <linux/interrupt.h>
19#include <linux/sched.h>
20#include <linux/uaccess.h>
21#include <linux/clk.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070022#include <linux/android_pmem.h>
23#include <linux/msm_rotator.h>
24#include <linux/io.h>
25#include <mach/msm_rotator_imem.h>
26#include <linux/ktime.h>
27#include <linux/workqueue.h>
28#include <linux/file.h>
29#include <linux/major.h>
30#include <linux/regulator/consumer.h>
Mitchel Humpherys9faf7e12012-09-06 10:33:12 -070031#include <linux/msm_ion.h>
Nagamalleswararao Ganjie1a9f872011-11-06 23:13:32 -080032#ifdef CONFIG_MSM_BUS_SCALING
33#include <mach/msm_bus.h>
34#include <mach/msm_bus_board.h>
35#endif
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -070036#include <mach/msm_subsystem_map.h>
37#include <mach/iommu_domains.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070038
39#define DRIVER_NAME "msm_rotator"
40
41#define MSM_ROTATOR_BASE (msm_rotator_dev->io_base)
42#define MSM_ROTATOR_INTR_ENABLE (MSM_ROTATOR_BASE+0x0020)
43#define MSM_ROTATOR_INTR_STATUS (MSM_ROTATOR_BASE+0x0024)
44#define MSM_ROTATOR_INTR_CLEAR (MSM_ROTATOR_BASE+0x0028)
45#define MSM_ROTATOR_START (MSM_ROTATOR_BASE+0x0030)
46#define MSM_ROTATOR_MAX_BURST_SIZE (MSM_ROTATOR_BASE+0x0050)
47#define MSM_ROTATOR_HW_VERSION (MSM_ROTATOR_BASE+0x0070)
Ravishangar Kalyanam30cf3eb2012-04-13 18:10:26 -070048#define MSM_ROTATOR_SW_RESET (MSM_ROTATOR_BASE+0x0074)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070049#define MSM_ROTATOR_SRC_SIZE (MSM_ROTATOR_BASE+0x1108)
50#define MSM_ROTATOR_SRCP0_ADDR (MSM_ROTATOR_BASE+0x110c)
51#define MSM_ROTATOR_SRCP1_ADDR (MSM_ROTATOR_BASE+0x1110)
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -070052#define MSM_ROTATOR_SRCP2_ADDR (MSM_ROTATOR_BASE+0x1114)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070053#define MSM_ROTATOR_SRC_YSTRIDE1 (MSM_ROTATOR_BASE+0x111c)
54#define MSM_ROTATOR_SRC_YSTRIDE2 (MSM_ROTATOR_BASE+0x1120)
55#define MSM_ROTATOR_SRC_FORMAT (MSM_ROTATOR_BASE+0x1124)
56#define MSM_ROTATOR_SRC_UNPACK_PATTERN1 (MSM_ROTATOR_BASE+0x1128)
57#define MSM_ROTATOR_SUB_BLOCK_CFG (MSM_ROTATOR_BASE+0x1138)
58#define MSM_ROTATOR_OUT_PACK_PATTERN1 (MSM_ROTATOR_BASE+0x1154)
59#define MSM_ROTATOR_OUTP0_ADDR (MSM_ROTATOR_BASE+0x1168)
60#define MSM_ROTATOR_OUTP1_ADDR (MSM_ROTATOR_BASE+0x116c)
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -070061#define MSM_ROTATOR_OUTP2_ADDR (MSM_ROTATOR_BASE+0x1170)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070062#define MSM_ROTATOR_OUT_YSTRIDE1 (MSM_ROTATOR_BASE+0x1178)
63#define MSM_ROTATOR_OUT_YSTRIDE2 (MSM_ROTATOR_BASE+0x117c)
64#define MSM_ROTATOR_SRC_XY (MSM_ROTATOR_BASE+0x1200)
65#define MSM_ROTATOR_SRC_IMAGE_SIZE (MSM_ROTATOR_BASE+0x1208)
66
67#define MSM_ROTATOR_MAX_ROT 0x07
68#define MSM_ROTATOR_MAX_H 0x1fff
69#define MSM_ROTATOR_MAX_W 0x1fff
70
71/* from lsb to msb */
72#define GET_PACK_PATTERN(a, x, y, z, bit) \
73 (((a)<<((bit)*3))|((x)<<((bit)*2))|((y)<<(bit))|(z))
74#define CLR_G 0x0
75#define CLR_B 0x1
76#define CLR_R 0x2
77#define CLR_ALPHA 0x3
78
79#define CLR_Y CLR_G
80#define CLR_CB CLR_B
81#define CLR_CR CLR_R
82
83#define ROTATIONS_TO_BITMASK(r) ((((r) & MDP_ROT_90) ? 1 : 0) | \
84 (((r) & MDP_FLIP_LR) ? 2 : 0) | \
85 (((r) & MDP_FLIP_UD) ? 4 : 0))
86
87#define IMEM_NO_OWNER -1;
88
89#define MAX_SESSIONS 16
90#define INVALID_SESSION -1
91#define VERSION_KEY_MASK 0xFFFFFF00
Adrian Salido-Moreno67273e52011-10-21 19:04:18 -070092#define MAX_DOWNSCALE_RATIO 3
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070093
Mayank Chopra012a8e72012-04-11 10:41:13 +053094#define ROTATOR_REVISION_V0 0
95#define ROTATOR_REVISION_V1 1
96#define ROTATOR_REVISION_V2 2
97#define ROTATOR_REVISION_NONE 0xffffffff
Mayank Chopra27147a72012-07-17 13:57:22 +053098#define BASE_ADDR(height, y_stride) ((height % 64) * y_stride)
99#define HW_BASE_ADDR(height, y_stride) (((dstp0_ystride >> 5) << 11) - \
100 ((dst_height & 0x3f) * dstp0_ystride))
Mayank Chopra012a8e72012-04-11 10:41:13 +0530101
102uint32_t rotator_hw_revision;
Olav Hauganef95ae32012-05-15 09:50:30 -0700103static char rot_iommu_split_domain;
Mayank Chopra012a8e72012-04-11 10:41:13 +0530104
105/*
106 * rotator_hw_revision:
107 * 0 == 7x30
108 * 1 == 8x60
109 * 2 == 8960
110 *
111 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700112struct tile_parm {
113 unsigned int width; /* tile's width */
114 unsigned int height; /* tile's height */
115 unsigned int row_tile_w; /* tiles per row's width */
116 unsigned int row_tile_h; /* tiles per row's height */
117};
118
Adrian Salido-Moreno88411862011-09-20 18:54:03 -0700119struct msm_rotator_mem_planes {
120 unsigned int num_planes;
121 unsigned int plane_size[4];
122 unsigned int total_size;
123};
124
125#define checkoffset(offset, size, max_size) \
126 ((size) > (max_size) || (offset) > ((max_size) - (size)))
127
Adrian Salido-Moreno5737db12012-04-02 15:22:08 -0700128struct msm_rotator_fd_info {
129 int pid;
130 int ref_cnt;
131 struct list_head list;
132};
133
Mayank Chopra27147a72012-07-17 13:57:22 +0530134struct msm_rotator_session {
135 struct msm_rotator_img_info img_info;
136 struct msm_rotator_fd_info fd_info;
137 int fast_yuv_enable;
138};
139
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700140struct msm_rotator_dev {
141 void __iomem *io_base;
142 int irq;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700143 struct clk *core_clk;
Mayank Chopra27147a72012-07-17 13:57:22 +0530144 struct msm_rotator_session *rot_session[MAX_SESSIONS];
Adrian Salido-Moreno5737db12012-04-02 15:22:08 -0700145 struct list_head fd_list;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146 struct clk *pclk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700147 int rot_clk_state;
148 struct regulator *regulator;
149 struct delayed_work rot_clk_work;
150 struct clk *imem_clk;
151 int imem_clk_state;
152 struct delayed_work imem_clk_work;
153 struct platform_device *pdev;
154 struct cdev cdev;
155 struct device *device;
156 struct class *class;
157 dev_t dev_num;
158 int processing;
159 int last_session_idx;
160 struct mutex rotator_lock;
161 struct mutex imem_lock;
162 int imem_owner;
163 wait_queue_head_t wq;
Naseer Ahmed18018602011-10-25 13:32:58 -0700164 struct ion_client *client;
Nagamalleswararao Ganjie1a9f872011-11-06 23:13:32 -0800165 #ifdef CONFIG_MSM_BUS_SCALING
166 uint32_t bus_client_handle;
167 #endif
Ken Zhangfebeabd2012-12-17 10:35:15 -0500168 u32 sec_mapped;
169 u32 mmu_clk_on;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700170};
171
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700172#define COMPONENT_5BITS 1
173#define COMPONENT_6BITS 2
174#define COMPONENT_8BITS 3
175
176static struct msm_rotator_dev *msm_rotator_dev;
177
178enum {
179 CLK_EN,
180 CLK_DIS,
181 CLK_SUSPEND,
182};
Ken Zhangfebeabd2012-12-17 10:35:15 -0500183struct res_mmu_clk {
184 char *mmu_clk_name;
185 struct clk *mmu_clk;
186};
187
188static struct res_mmu_clk rot_mmu_clks[] = {
189 {"mdp_iommu_clk"}, {"rot_iommu_clk"},
190 {"vcodec_iommu0_clk"}, {"vcodec_iommu1_clk"},
191 {"smmu_iface_clk"}
192};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700193
Ravishangar Kalyanam1eca3522012-05-31 18:02:24 -0700194int msm_rotator_iommu_map_buf(int mem_id, int domain,
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -0700195 unsigned long *start, unsigned long *len,
Ravishangar Kalyanam1eca3522012-05-31 18:02:24 -0700196 struct ion_handle **pihdl, unsigned int secure)
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -0700197{
198 if (!msm_rotator_dev->client)
199 return -EINVAL;
200
Laura Abbottb14ed962012-01-30 14:18:08 -0800201 *pihdl = ion_import_dma_buf(msm_rotator_dev->client, mem_id);
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -0700202 if (IS_ERR_OR_NULL(*pihdl)) {
Laura Abbottb14ed962012-01-30 14:18:08 -0800203 pr_err("ion_import_dma_buf() failed\n");
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -0700204 return PTR_ERR(*pihdl);
205 }
taeyol.kim69400442012-08-03 19:15:10 -0700206 pr_debug("%s(): ion_hdl %p, ion_fd %d\n", __func__, *pihdl, mem_id);
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -0700207
Ravishangar Kalyanam1eca3522012-05-31 18:02:24 -0700208 if (rot_iommu_split_domain) {
209 if (secure) {
210 if (ion_phys(msm_rotator_dev->client,
211 *pihdl, start, (unsigned *)len)) {
212 pr_err("%s:%d: ion_phys map failed\n",
213 __func__, __LINE__);
214 return -ENOMEM;
215 }
216 } else {
217 if (ion_map_iommu(msm_rotator_dev->client,
218 *pihdl, domain, GEN_POOL,
219 SZ_4K, 0, start, len, 0,
220 ION_IOMMU_UNMAP_DELAYED)) {
221 pr_err("ion_map_iommu() failed\n");
222 return -EINVAL;
223 }
224 }
225 } else {
226 if (ion_map_iommu(msm_rotator_dev->client,
227 *pihdl, ROTATOR_SRC_DOMAIN, GEN_POOL,
228 SZ_4K, 0, start, len, 0, ION_IOMMU_UNMAP_DELAYED)) {
229 pr_err("ion_map_iommu() failed\n");
230 return -EINVAL;
231 }
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -0700232 }
233
234 pr_debug("%s(): mem_id %d, start 0x%lx, len 0x%lx\n",
235 __func__, mem_id, *start, *len);
236 return 0;
237}
238
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700239int msm_rotator_imem_allocate(int requestor)
240{
241 int rc = 0;
242
243#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
244 switch (requestor) {
245 case ROTATOR_REQUEST:
246 if (mutex_trylock(&msm_rotator_dev->imem_lock)) {
247 msm_rotator_dev->imem_owner = ROTATOR_REQUEST;
248 rc = 1;
249 } else
250 rc = 0;
251 break;
252 case JPEG_REQUEST:
253 mutex_lock(&msm_rotator_dev->imem_lock);
254 msm_rotator_dev->imem_owner = JPEG_REQUEST;
255 rc = 1;
256 break;
257 default:
258 rc = 0;
259 }
260#else
261 if (requestor == JPEG_REQUEST)
262 rc = 1;
263#endif
264 if (rc == 1) {
265 cancel_delayed_work(&msm_rotator_dev->imem_clk_work);
266 if (msm_rotator_dev->imem_clk_state != CLK_EN
267 && msm_rotator_dev->imem_clk) {
Ravishangar Kalyanamd16485d2012-03-21 16:51:26 -0700268 clk_prepare_enable(msm_rotator_dev->imem_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700269 msm_rotator_dev->imem_clk_state = CLK_EN;
270 }
271 }
272
273 return rc;
274}
275EXPORT_SYMBOL(msm_rotator_imem_allocate);
276
277void msm_rotator_imem_free(int requestor)
278{
279#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
280 if (msm_rotator_dev->imem_owner == requestor) {
281 schedule_delayed_work(&msm_rotator_dev->imem_clk_work, HZ);
282 mutex_unlock(&msm_rotator_dev->imem_lock);
283 }
284#else
285 if (requestor == JPEG_REQUEST)
286 schedule_delayed_work(&msm_rotator_dev->imem_clk_work, HZ);
287#endif
288}
289EXPORT_SYMBOL(msm_rotator_imem_free);
290
291static void msm_rotator_imem_clk_work_f(struct work_struct *work)
292{
293#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
294 if (mutex_trylock(&msm_rotator_dev->imem_lock)) {
295 if (msm_rotator_dev->imem_clk_state == CLK_EN
296 && msm_rotator_dev->imem_clk) {
Ravishangar Kalyanamd16485d2012-03-21 16:51:26 -0700297 clk_disable_unprepare(msm_rotator_dev->imem_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700298 msm_rotator_dev->imem_clk_state = CLK_DIS;
299 } else if (msm_rotator_dev->imem_clk_state == CLK_SUSPEND)
300 msm_rotator_dev->imem_clk_state = CLK_DIS;
301 mutex_unlock(&msm_rotator_dev->imem_lock);
302 }
303#endif
304}
305
306/* enable clocks needed by rotator block */
307static void enable_rot_clks(void)
308{
309 if (msm_rotator_dev->regulator)
310 regulator_enable(msm_rotator_dev->regulator);
311 if (msm_rotator_dev->core_clk != NULL)
Ravishangar Kalyanamd16485d2012-03-21 16:51:26 -0700312 clk_prepare_enable(msm_rotator_dev->core_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700313 if (msm_rotator_dev->pclk != NULL)
Ravishangar Kalyanamd16485d2012-03-21 16:51:26 -0700314 clk_prepare_enable(msm_rotator_dev->pclk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700315}
316
317/* disable clocks needed by rotator block */
318static void disable_rot_clks(void)
319{
320 if (msm_rotator_dev->core_clk != NULL)
Ravishangar Kalyanamd16485d2012-03-21 16:51:26 -0700321 clk_disable_unprepare(msm_rotator_dev->core_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700322 if (msm_rotator_dev->pclk != NULL)
Ravishangar Kalyanamd16485d2012-03-21 16:51:26 -0700323 clk_disable_unprepare(msm_rotator_dev->pclk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700324 if (msm_rotator_dev->regulator)
325 regulator_disable(msm_rotator_dev->regulator);
326}
327
328static void msm_rotator_rot_clk_work_f(struct work_struct *work)
329{
330 if (mutex_trylock(&msm_rotator_dev->rotator_lock)) {
331 if (msm_rotator_dev->rot_clk_state == CLK_EN) {
332 disable_rot_clks();
333 msm_rotator_dev->rot_clk_state = CLK_DIS;
334 } else if (msm_rotator_dev->rot_clk_state == CLK_SUSPEND)
335 msm_rotator_dev->rot_clk_state = CLK_DIS;
336 mutex_unlock(&msm_rotator_dev->rotator_lock);
337 }
338}
339
340static irqreturn_t msm_rotator_isr(int irq, void *dev_id)
341{
342 if (msm_rotator_dev->processing) {
343 msm_rotator_dev->processing = 0;
344 wake_up(&msm_rotator_dev->wq);
345 } else
346 printk(KERN_WARNING "%s: unexpected interrupt\n", DRIVER_NAME);
347
348 return IRQ_HANDLED;
349}
350
Adrian Salido-Moreno88411862011-09-20 18:54:03 -0700351static unsigned int tile_size(unsigned int src_width,
352 unsigned int src_height,
353 const struct tile_parm *tp)
354{
355 unsigned int tile_w, tile_h;
356 unsigned int row_num_w, row_num_h;
357 tile_w = tp->width * tp->row_tile_w;
358 tile_h = tp->height * tp->row_tile_h;
359 row_num_w = (src_width + tile_w - 1) / tile_w;
360 row_num_h = (src_height + tile_h - 1) / tile_h;
361 return ((row_num_w * row_num_h * tile_w * tile_h) + 8191) & ~8191;
362}
363
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700364static int get_bpp(int format)
365{
366 switch (format) {
367 case MDP_RGB_565:
368 case MDP_BGR_565:
369 return 2;
370
371 case MDP_XRGB_8888:
372 case MDP_ARGB_8888:
373 case MDP_RGBA_8888:
374 case MDP_BGRA_8888:
375 case MDP_RGBX_8888:
376 return 4;
377
378 case MDP_Y_CBCR_H2V2:
379 case MDP_Y_CRCB_H2V2:
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -0700380 case MDP_Y_CB_CR_H2V2:
381 case MDP_Y_CR_CB_H2V2:
Pradeep Jilagam9b4a6be2011-10-03 17:19:20 +0530382 case MDP_Y_CR_CB_GH2V2:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700383 case MDP_Y_CRCB_H2V2_TILE:
384 case MDP_Y_CBCR_H2V2_TILE:
385 return 1;
386
387 case MDP_RGB_888:
Adrian Salido-Morenoeeb06c72011-08-15 10:41:35 -0700388 case MDP_YCBCR_H1V1:
389 case MDP_YCRCB_H1V1:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700390 return 3;
391
392 case MDP_YCRYCB_H2V1:
393 return 2;/* YCrYCb interleave */
394
395 case MDP_Y_CRCB_H2V1:
396 case MDP_Y_CBCR_H2V1:
397 return 1;
398
399 default:
400 return -1;
401 }
402
403}
404
Adrian Salido-Moreno88411862011-09-20 18:54:03 -0700405static int msm_rotator_get_plane_sizes(uint32_t format, uint32_t w, uint32_t h,
406 struct msm_rotator_mem_planes *p)
407{
408 /*
409 * each row of samsung tile consists of two tiles in height
410 * and two tiles in width which means width should align to
411 * 64 x 2 bytes and height should align to 32 x 2 bytes.
412 * video decoder generate two tiles in width and one tile
413 * in height which ends up height align to 32 X 1 bytes.
414 */
415 const struct tile_parm tile = {64, 32, 2, 1};
416 int i;
417
418 if (p == NULL)
419 return -EINVAL;
420
421 if ((w > MSM_ROTATOR_MAX_W) || (h > MSM_ROTATOR_MAX_H))
422 return -ERANGE;
423
424 memset(p, 0, sizeof(*p));
425
426 switch (format) {
427 case MDP_XRGB_8888:
428 case MDP_ARGB_8888:
429 case MDP_RGBA_8888:
430 case MDP_BGRA_8888:
431 case MDP_RGBX_8888:
432 case MDP_RGB_888:
433 case MDP_RGB_565:
434 case MDP_BGR_565:
435 case MDP_YCRYCB_H2V1:
Kyong Hwa Baeebf19192012-05-09 16:31:05 -0700436 case MDP_YCBCR_H1V1:
437 case MDP_YCRCB_H1V1:
Adrian Salido-Moreno88411862011-09-20 18:54:03 -0700438 p->num_planes = 1;
439 p->plane_size[0] = w * h * get_bpp(format);
440 break;
441 case MDP_Y_CRCB_H2V1:
442 case MDP_Y_CBCR_H2V1:
Mayank Chopra797bdb72012-03-03 06:29:40 +0530443 case MDP_Y_CRCB_H1V2:
Adrian Salido-Moreno88411862011-09-20 18:54:03 -0700444 p->num_planes = 2;
445 p->plane_size[0] = w * h;
446 p->plane_size[1] = w * h;
447 break;
448 case MDP_Y_CBCR_H2V2:
449 case MDP_Y_CRCB_H2V2:
450 p->num_planes = 2;
451 p->plane_size[0] = w * h;
452 p->plane_size[1] = w * h / 2;
453 break;
454 case MDP_Y_CRCB_H2V2_TILE:
455 case MDP_Y_CBCR_H2V2_TILE:
456 p->num_planes = 2;
457 p->plane_size[0] = tile_size(w, h, &tile);
458 p->plane_size[1] = tile_size(w, h/2, &tile);
459 break;
460 case MDP_Y_CB_CR_H2V2:
461 case MDP_Y_CR_CB_H2V2:
462 p->num_planes = 3;
463 p->plane_size[0] = w * h;
464 p->plane_size[1] = (w / 2) * (h / 2);
465 p->plane_size[2] = (w / 2) * (h / 2);
466 break;
467 case MDP_Y_CR_CB_GH2V2:
468 p->num_planes = 3;
469 p->plane_size[0] = ALIGN(w, 16) * h;
470 p->plane_size[1] = ALIGN(w / 2, 16) * (h / 2);
471 p->plane_size[2] = ALIGN(w / 2, 16) * (h / 2);
472 break;
473 default:
474 return -EINVAL;
475 }
476
477 for (i = 0; i < p->num_planes; i++)
478 p->total_size += p->plane_size[i];
479
480 return 0;
481}
482
Mayank Chopra27147a72012-07-17 13:57:22 +0530483/* Checking invalid destination image size on FAST YUV for YUV420PP(NV12) with
484 * HW issue for rotation 90 + U/D filp + with/without flip operation
485 * (rotation 90 + U/D + L/R flip is rotation 270 degree option) and pix_rot
486 * block issue with tile line size is 4.
487 *
488 * Rotator structure is:
489 * if Fetch input image: W x H,
490 * Downscale: W` x H` = W/ScaleHor(2, 4 or 8) x H/ScaleVert(2, 4 or 8)
491 * Rotated output : W`` x H`` = (W` x H`) or (H` x W`) depends on "Rotation 90
492 * degree option"
493 *
494 * Pack: W`` x H``
495 *
496 * Rotator source ROI image width restriction is applied to W x H (case a,
497 * image resolution before downscaling)
498 *
499 * Packer source Image width/ height restriction are applied to W`` x H``
500 * (case c, image resolution after rotation)
501 *
502 * Supertile (64 x 8) and YUV (2 x 2) alignment restriction should be
503 * applied to the W x H (case a). Input image should be at least (2 x 2).
504 *
505 * "Support If packer source image height <= 256, multiple of 8", this
506 * restriction should be applied to the rotated image (W`` x H``)
507 */
508
509uint32_t fast_yuv_invalid_size_checker(unsigned char rot_mode,
510 uint32_t src_width,
511 uint32_t dst_width,
512 uint32_t dst_height,
513 uint32_t dstp0_ystride,
514 uint32_t is_planar420)
515{
516 uint32_t hw_limit;
517
518 hw_limit = is_planar420 ? 512 : 256;
519
520 /* checking image constaints for missing EOT event from pix_rot block */
521 if ((src_width > hw_limit) && ((src_width % (hw_limit / 2)) == 8))
522 return -EINVAL;
523
524 if (rot_mode & MDP_ROT_90) {
525
526 /* if rotation 90 degree on fast yuv
527 * rotator image input width has to be multiple of 8
528 * rotator image input height has to be multiple of 8
529 */
530 if (((dst_width % 8) != 0) || ((dst_height % 8) != 0))
531 return -EINVAL;
532
533 if ((rot_mode & MDP_FLIP_UD) ||
534 (rot_mode & (MDP_FLIP_UD | MDP_FLIP_LR))) {
535
536 /* image constraint checking for wrong address
537 * generation HW issue for Y plane checking
538 */
539 if (((dst_height % 64) != 0) &&
540 ((dst_height / 64) >= 4)) {
541
542 /* compare golden logic for second
543 * tile base address generation in row
544 * with actual HW implementation
545 */
546 if (BASE_ADDR(dst_height, dstp0_ystride) !=
547 HW_BASE_ADDR(dst_height, dstp0_ystride))
548 return -EINVAL;
549 }
550
551 if (is_planar420) {
552 dst_width = dst_width / 2;
553 dstp0_ystride = dstp0_ystride / 2;
554 }
555
556 dst_height = dst_height / 2;
557
558 /* image constraint checking for wrong
559 * address generation HW issue. for
560 * U/V (P) or UV (PP) plane checking
561 */
562 if (((dst_height % 64) != 0) && ((dst_height / 64) >=
563 (hw_limit / 128))) {
564
565 /* compare golden logic for
566 * second tile base address
567 * generation in row with
568 * actual HW implementation
569 */
570 if (BASE_ADDR(dst_height, dstp0_ystride) !=
571 HW_BASE_ADDR(dst_height, dstp0_ystride))
572 return -EINVAL;
573 }
574 }
575 } else {
576 /* if NOT applying rotation 90 degree on fast yuv,
577 * rotator image input width has to be multiple of 8
578 * rotator image input height has to be multiple of 2
579 */
580 if (((dst_width % 8) != 0) || ((dst_height % 2) != 0))
581 return -EINVAL;
582 }
583
584 return 0;
585}
586
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700587static int msm_rotator_ycxcx_h2v1(struct msm_rotator_img_info *info,
588 unsigned int in_paddr,
589 unsigned int out_paddr,
590 unsigned int use_imem,
591 int new_session,
592 unsigned int in_chroma_paddr,
593 unsigned int out_chroma_paddr)
594{
595 int bpp;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700596
597 if (info->src.format != info->dst.format)
598 return -EINVAL;
599
600 bpp = get_bpp(info->src.format);
601 if (bpp < 0)
602 return -ENOTTY;
603
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700604 iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
Adrian Salido-Moreno88411862011-09-20 18:54:03 -0700605 iowrite32(in_chroma_paddr, MSM_ROTATOR_SRCP1_ADDR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700606 iowrite32(out_paddr +
607 ((info->dst_y * info->dst.width) + info->dst_x),
608 MSM_ROTATOR_OUTP0_ADDR);
Adrian Salido-Moreno88411862011-09-20 18:54:03 -0700609 iowrite32(out_chroma_paddr +
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700610 ((info->dst_y * info->dst.width) + info->dst_x),
611 MSM_ROTATOR_OUTP1_ADDR);
612
613 if (new_session) {
614 iowrite32(info->src.width |
615 info->src.width << 16,
616 MSM_ROTATOR_SRC_YSTRIDE1);
617 if (info->rotations & MDP_ROT_90)
618 iowrite32(info->dst.width |
619 info->dst.width*2 << 16,
620 MSM_ROTATOR_OUT_YSTRIDE1);
621 else
622 iowrite32(info->dst.width |
623 info->dst.width << 16,
624 MSM_ROTATOR_OUT_YSTRIDE1);
625 if (info->src.format == MDP_Y_CBCR_H2V1) {
626 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
627 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
628 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
629 MSM_ROTATOR_OUT_PACK_PATTERN1);
630 } else {
631 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
632 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
633 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
634 MSM_ROTATOR_OUT_PACK_PATTERN1);
635 }
636 iowrite32((1 << 18) | /* chroma sampling 1=H2V1 */
637 (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
Adrian Salido-Moreno67273e52011-10-21 19:04:18 -0700638 1 << 8 | /* ROT_EN */
639 info->downscale_ratio << 2 | /* downscale v ratio */
640 info->downscale_ratio, /* downscale h ratio */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700641 MSM_ROTATOR_SUB_BLOCK_CFG);
642 iowrite32(0 << 29 | /* frame format 0 = linear */
643 (use_imem ? 0 : 1) << 22 | /* tile size */
644 2 << 19 | /* fetch planes 2 = pseudo */
645 0 << 18 | /* unpack align */
646 1 << 17 | /* unpack tight */
647 1 << 13 | /* unpack count 0=1 component */
648 (bpp-1) << 9 | /* src Bpp 0=1 byte ... */
649 0 << 8 | /* has alpha */
650 0 << 6 | /* alpha bits 3=8bits */
651 3 << 4 | /* R/Cr bits 1=5 2=6 3=8 */
652 3 << 2 | /* B/Cb bits 1=5 2=6 3=8 */
653 3 << 0, /* G/Y bits 1=5 2=6 3=8 */
654 MSM_ROTATOR_SRC_FORMAT);
655 }
656
657 return 0;
658}
659
660static int msm_rotator_ycxcx_h2v2(struct msm_rotator_img_info *info,
661 unsigned int in_paddr,
662 unsigned int out_paddr,
663 unsigned int use_imem,
664 int new_session,
665 unsigned int in_chroma_paddr,
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -0700666 unsigned int out_chroma_paddr,
Mayank Chopra27147a72012-07-17 13:57:22 +0530667 unsigned int in_chroma2_paddr,
668 unsigned int out_chroma2_paddr)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700669{
Adrian Salido-Moreno88411862011-09-20 18:54:03 -0700670 uint32_t dst_format;
671 int is_tile = 0;
Mayank Chopra27147a72012-07-17 13:57:22 +0530672 struct msm_rotator_session *rot_ssn =
673 container_of(info, struct msm_rotator_session, img_info);
674 int fast_yuv_en = rot_ssn->fast_yuv_enable;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700675
Adrian Salido-Moreno88411862011-09-20 18:54:03 -0700676 switch (info->src.format) {
677 case MDP_Y_CRCB_H2V2_TILE:
678 is_tile = 1;
Mayank Chopra27147a72012-07-17 13:57:22 +0530679 dst_format = MDP_Y_CRCB_H2V2;
680 break;
Adrian Salido-Moreno88411862011-09-20 18:54:03 -0700681 case MDP_Y_CR_CB_H2V2:
682 case MDP_Y_CR_CB_GH2V2:
Mayank Chopra27147a72012-07-17 13:57:22 +0530683 if (fast_yuv_en) {
684 dst_format = info->src.format;
685 break;
686 }
Adrian Salido-Moreno88411862011-09-20 18:54:03 -0700687 case MDP_Y_CRCB_H2V2:
688 dst_format = MDP_Y_CRCB_H2V2;
689 break;
Mayank Chopra27147a72012-07-17 13:57:22 +0530690 case MDP_Y_CB_CR_H2V2:
691 if (fast_yuv_en) {
692 dst_format = info->src.format;
693 break;
694 }
695 dst_format = MDP_Y_CBCR_H2V2;
696 break;
Adrian Salido-Moreno88411862011-09-20 18:54:03 -0700697 case MDP_Y_CBCR_H2V2_TILE:
698 is_tile = 1;
Adrian Salido-Moreno88411862011-09-20 18:54:03 -0700699 case MDP_Y_CBCR_H2V2:
700 dst_format = MDP_Y_CBCR_H2V2;
701 break;
702 default:
703 return -EINVAL;
704 }
705 if (info->dst.format != dst_format)
706 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700707
Adrian Salido-Moreno19caf152012-01-03 18:46:25 -0800708 /* rotator expects YCbCr for planar input format */
Mayank Chopra012a8e72012-04-11 10:41:13 +0530709 if ((info->src.format == MDP_Y_CR_CB_H2V2 ||
710 info->src.format == MDP_Y_CR_CB_GH2V2) &&
711 rotator_hw_revision < ROTATOR_REVISION_V2)
Adrian Salido-Moreno19caf152012-01-03 18:46:25 -0800712 swap(in_chroma_paddr, in_chroma2_paddr);
713
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700714 iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
Adrian Salido-Moreno88411862011-09-20 18:54:03 -0700715 iowrite32(in_chroma_paddr, MSM_ROTATOR_SRCP1_ADDR);
716 iowrite32(in_chroma2_paddr, MSM_ROTATOR_SRCP2_ADDR);
717
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700718 iowrite32(out_paddr +
719 ((info->dst_y * info->dst.width) + info->dst_x),
720 MSM_ROTATOR_OUTP0_ADDR);
Adrian Salido-Moreno88411862011-09-20 18:54:03 -0700721 iowrite32(out_chroma_paddr +
Mayank Chopra27147a72012-07-17 13:57:22 +0530722 (((info->dst_y * info->dst.width)/2) + info->dst_x),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700723 MSM_ROTATOR_OUTP1_ADDR);
Mayank Chopra27147a72012-07-17 13:57:22 +0530724 if (out_chroma2_paddr)
725 iowrite32(out_chroma2_paddr +
726 (((info->dst_y * info->dst.width)/2) + info->dst_x),
727 MSM_ROTATOR_OUTP2_ADDR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700728
729 if (new_session) {
Adrian Salido-Moreno88411862011-09-20 18:54:03 -0700730 if (in_chroma2_paddr) {
731 if (info->src.format == MDP_Y_CR_CB_GH2V2) {
Pradeep Jilagam9b4a6be2011-10-03 17:19:20 +0530732 iowrite32(ALIGN(info->src.width, 16) |
733 ALIGN((info->src.width / 2), 16) << 16,
734 MSM_ROTATOR_SRC_YSTRIDE1);
735 iowrite32(ALIGN((info->src.width / 2), 16),
736 MSM_ROTATOR_SRC_YSTRIDE2);
737 } else {
738 iowrite32(info->src.width |
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -0700739 (info->src.width / 2) << 16,
740 MSM_ROTATOR_SRC_YSTRIDE1);
Pradeep Jilagam9b4a6be2011-10-03 17:19:20 +0530741 iowrite32((info->src.width / 2),
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -0700742 MSM_ROTATOR_SRC_YSTRIDE2);
Pradeep Jilagam9b4a6be2011-10-03 17:19:20 +0530743 }
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -0700744 } else {
745 iowrite32(info->src.width |
746 info->src.width << 16,
747 MSM_ROTATOR_SRC_YSTRIDE1);
748 }
Mayank Chopra27147a72012-07-17 13:57:22 +0530749 if (out_chroma2_paddr) {
750 if (info->dst.format == MDP_Y_CR_CB_GH2V2) {
751 iowrite32(ALIGN(info->dst.width, 16) |
752 ALIGN((info->dst.width / 2), 16) << 16,
753 MSM_ROTATOR_OUT_YSTRIDE1);
754 iowrite32(ALIGN((info->dst.width / 2), 16),
755 MSM_ROTATOR_OUT_YSTRIDE2);
756 } else {
757 iowrite32(info->dst.width |
758 info->dst.width/2 << 16,
759 MSM_ROTATOR_OUT_YSTRIDE1);
760 iowrite32(info->dst.width/2,
761 MSM_ROTATOR_OUT_YSTRIDE2);
762 }
763 } else {
764 iowrite32(info->dst.width |
765 info->dst.width << 16,
766 MSM_ROTATOR_OUT_YSTRIDE1);
767 }
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -0700768
Mayank Chopra27147a72012-07-17 13:57:22 +0530769 if (dst_format == MDP_Y_CBCR_H2V2 ||
770 dst_format == MDP_Y_CB_CR_H2V2) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700771 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
772 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
773 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
774 MSM_ROTATOR_OUT_PACK_PATTERN1);
775 } else {
776 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
777 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
778 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
779 MSM_ROTATOR_OUT_PACK_PATTERN1);
780 }
Mayank Chopra27147a72012-07-17 13:57:22 +0530781
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700782 iowrite32((3 << 18) | /* chroma sampling 3=4:2:0 */
783 (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
Adrian Salido-Moreno67273e52011-10-21 19:04:18 -0700784 1 << 8 | /* ROT_EN */
Mayank Chopra27147a72012-07-17 13:57:22 +0530785 fast_yuv_en << 4 | /*fast YUV*/
Adrian Salido-Moreno67273e52011-10-21 19:04:18 -0700786 info->downscale_ratio << 2 | /* downscale v ratio */
787 info->downscale_ratio, /* downscale h ratio */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700788 MSM_ROTATOR_SUB_BLOCK_CFG);
Adrian Salido-Moreno88411862011-09-20 18:54:03 -0700789
790 iowrite32((is_tile ? 2 : 0) << 29 | /* frame format */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700791 (use_imem ? 0 : 1) << 22 | /* tile size */
Adrian Salido-Moreno88411862011-09-20 18:54:03 -0700792 (in_chroma2_paddr ? 1 : 2) << 19 | /* fetch planes */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700793 0 << 18 | /* unpack align */
794 1 << 17 | /* unpack tight */
795 1 << 13 | /* unpack count 0=1 component */
Adrian Salido-Moreno88411862011-09-20 18:54:03 -0700796 0 << 9 | /* src Bpp 0=1 byte ... */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700797 0 << 8 | /* has alpha */
798 0 << 6 | /* alpha bits 3=8bits */
799 3 << 4 | /* R/Cr bits 1=5 2=6 3=8 */
800 3 << 2 | /* B/Cb bits 1=5 2=6 3=8 */
801 3 << 0, /* G/Y bits 1=5 2=6 3=8 */
802 MSM_ROTATOR_SRC_FORMAT);
803 }
804 return 0;
805}
806
807static int msm_rotator_ycrycb(struct msm_rotator_img_info *info,
808 unsigned int in_paddr,
809 unsigned int out_paddr,
810 unsigned int use_imem,
Mayank Chopra732dcd62012-01-09 20:53:39 +0530811 int new_session,
812 unsigned int out_chroma_paddr)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700813{
814 int bpp;
Mayank Chopra732dcd62012-01-09 20:53:39 +0530815 uint32_t dst_format;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700816
Mayank Chopra797bdb72012-03-03 06:29:40 +0530817 if (info->src.format == MDP_YCRYCB_H2V1) {
818 if (info->rotations & MDP_ROT_90)
819 dst_format = MDP_Y_CRCB_H1V2;
820 else
821 dst_format = MDP_Y_CRCB_H2V1;
822 } else
Mayank Chopra732dcd62012-01-09 20:53:39 +0530823 return -EINVAL;
824
825 if (info->dst.format != dst_format)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700826 return -EINVAL;
827
828 bpp = get_bpp(info->src.format);
829 if (bpp < 0)
830 return -ENOTTY;
831
832 iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
833 iowrite32(out_paddr +
834 ((info->dst_y * info->dst.width) + info->dst_x),
835 MSM_ROTATOR_OUTP0_ADDR);
Mayank Chopra732dcd62012-01-09 20:53:39 +0530836 iowrite32(out_chroma_paddr +
837 ((info->dst_y * info->dst.width)/2 + info->dst_x),
838 MSM_ROTATOR_OUTP1_ADDR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700839
840 if (new_session) {
Mayank Chopra732dcd62012-01-09 20:53:39 +0530841 iowrite32(info->src.width * bpp,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700842 MSM_ROTATOR_SRC_YSTRIDE1);
Mayank Chopra732dcd62012-01-09 20:53:39 +0530843 if (info->rotations & MDP_ROT_90)
844 iowrite32(info->dst.width |
845 (info->dst.width*2) << 16,
846 MSM_ROTATOR_OUT_YSTRIDE1);
847 else
848 iowrite32(info->dst.width |
849 (info->dst.width) << 16,
850 MSM_ROTATOR_OUT_YSTRIDE1);
851
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700852 iowrite32(GET_PACK_PATTERN(CLR_Y, CLR_CR, CLR_Y, CLR_CB, 8),
853 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
Mayank Chopra732dcd62012-01-09 20:53:39 +0530854 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700855 MSM_ROTATOR_OUT_PACK_PATTERN1);
856 iowrite32((1 << 18) | /* chroma sampling 1=H2V1 */
857 (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
Adrian Salido-Moreno67273e52011-10-21 19:04:18 -0700858 1 << 8 | /* ROT_EN */
859 info->downscale_ratio << 2 | /* downscale v ratio */
860 info->downscale_ratio, /* downscale h ratio */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700861 MSM_ROTATOR_SUB_BLOCK_CFG);
862 iowrite32(0 << 29 | /* frame format 0 = linear */
863 (use_imem ? 0 : 1) << 22 | /* tile size */
864 0 << 19 | /* fetch planes 0=interleaved */
865 0 << 18 | /* unpack align */
866 1 << 17 | /* unpack tight */
867 3 << 13 | /* unpack count 0=1 component */
868 (bpp-1) << 9 | /* src Bpp 0=1 byte ... */
869 0 << 8 | /* has alpha */
870 0 << 6 | /* alpha bits 3=8bits */
871 3 << 4 | /* R/Cr bits 1=5 2=6 3=8 */
872 3 << 2 | /* B/Cb bits 1=5 2=6 3=8 */
873 3 << 0, /* G/Y bits 1=5 2=6 3=8 */
874 MSM_ROTATOR_SRC_FORMAT);
875 }
876
877 return 0;
878}
879
880static int msm_rotator_rgb_types(struct msm_rotator_img_info *info,
881 unsigned int in_paddr,
882 unsigned int out_paddr,
883 unsigned int use_imem,
884 int new_session)
885{
886 int bpp, abits, rbits, gbits, bbits;
887
888 if (info->src.format != info->dst.format)
889 return -EINVAL;
890
891 bpp = get_bpp(info->src.format);
892 if (bpp < 0)
893 return -ENOTTY;
894
895 iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
896 iowrite32(out_paddr +
897 ((info->dst_y * info->dst.width) + info->dst_x) * bpp,
898 MSM_ROTATOR_OUTP0_ADDR);
899
900 if (new_session) {
901 iowrite32(info->src.width * bpp, MSM_ROTATOR_SRC_YSTRIDE1);
902 iowrite32(info->dst.width * bpp, MSM_ROTATOR_OUT_YSTRIDE1);
903 iowrite32((0 << 18) | /* chroma sampling 0=rgb */
904 (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
Adrian Salido-Moreno67273e52011-10-21 19:04:18 -0700905 1 << 8 | /* ROT_EN */
906 info->downscale_ratio << 2 | /* downscale v ratio */
907 info->downscale_ratio, /* downscale h ratio */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700908 MSM_ROTATOR_SUB_BLOCK_CFG);
909 switch (info->src.format) {
910 case MDP_RGB_565:
911 iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
912 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
913 iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
914 MSM_ROTATOR_OUT_PACK_PATTERN1);
915 abits = 0;
916 rbits = COMPONENT_5BITS;
917 gbits = COMPONENT_6BITS;
918 bbits = COMPONENT_5BITS;
919 break;
920
921 case MDP_BGR_565:
922 iowrite32(GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
923 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
924 iowrite32(GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
925 MSM_ROTATOR_OUT_PACK_PATTERN1);
926 abits = 0;
927 rbits = COMPONENT_5BITS;
928 gbits = COMPONENT_6BITS;
929 bbits = COMPONENT_5BITS;
930 break;
931
932 case MDP_RGB_888:
Adrian Salido-Morenoeeb06c72011-08-15 10:41:35 -0700933 case MDP_YCBCR_H1V1:
934 case MDP_YCRCB_H1V1:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700935 iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
936 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
937 iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
938 MSM_ROTATOR_OUT_PACK_PATTERN1);
939 abits = 0;
940 rbits = COMPONENT_8BITS;
941 gbits = COMPONENT_8BITS;
942 bbits = COMPONENT_8BITS;
943 break;
944
945 case MDP_ARGB_8888:
946 case MDP_RGBA_8888:
947 case MDP_XRGB_8888:
948 case MDP_RGBX_8888:
949 iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G,
950 CLR_B, 8),
951 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
952 iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G,
953 CLR_B, 8),
954 MSM_ROTATOR_OUT_PACK_PATTERN1);
955 abits = COMPONENT_8BITS;
956 rbits = COMPONENT_8BITS;
957 gbits = COMPONENT_8BITS;
958 bbits = COMPONENT_8BITS;
959 break;
960
961 case MDP_BGRA_8888:
962 iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G,
963 CLR_R, 8),
964 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
965 iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G,
966 CLR_R, 8),
967 MSM_ROTATOR_OUT_PACK_PATTERN1);
968 abits = COMPONENT_8BITS;
969 rbits = COMPONENT_8BITS;
970 gbits = COMPONENT_8BITS;
971 bbits = COMPONENT_8BITS;
972 break;
973
974 default:
975 return -EINVAL;
976 }
977 iowrite32(0 << 29 | /* frame format 0 = linear */
978 (use_imem ? 0 : 1) << 22 | /* tile size */
979 0 << 19 | /* fetch planes 0=interleaved */
980 0 << 18 | /* unpack align */
981 1 << 17 | /* unpack tight */
982 (abits ? 3 : 2) << 13 | /* unpack count 0=1 comp */
983 (bpp-1) << 9 | /* src Bpp 0=1 byte ... */
984 (abits ? 1 : 0) << 8 | /* has alpha */
985 abits << 6 | /* alpha bits 3=8bits */
986 rbits << 4 | /* R/Cr bits 1=5 2=6 3=8 */
987 bbits << 2 | /* B/Cb bits 1=5 2=6 3=8 */
988 gbits << 0, /* G/Y bits 1=5 2=6 3=8 */
989 MSM_ROTATOR_SRC_FORMAT);
990 }
991
992 return 0;
993}
994
Ravishangar Kalyanam1eca3522012-05-31 18:02:24 -0700995static int get_img(struct msmfb_data *fbd, int domain,
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -0700996 unsigned long *start, unsigned long *len, struct file **p_file,
Ravishangar Kalyanam1eca3522012-05-31 18:02:24 -0700997 int *p_need, struct ion_handle **p_ihdl, unsigned int secure)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700998{
999 int ret = 0;
1000#ifdef CONFIG_FB
Naseer Ahmed18018602011-10-25 13:32:58 -07001001 struct file *file = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001002 int put_needed, fb_num;
1003#endif
1004#ifdef CONFIG_ANDROID_PMEM
1005 unsigned long vstart;
1006#endif
1007
Kuogee Hsiehbeaa9352012-02-24 16:59:26 -08001008 *p_need = 0;
1009
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001010#ifdef CONFIG_FB
Naseer Ahmed18018602011-10-25 13:32:58 -07001011 if (fbd->flags & MDP_MEMORY_ID_TYPE_FB) {
1012 file = fget_light(fbd->memory_id, &put_needed);
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -07001013 if (file == NULL) {
1014 pr_err("fget_light returned NULL\n");
Naseer Ahmed18018602011-10-25 13:32:58 -07001015 return -EINVAL;
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -07001016 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001017
Naseer Ahmed18018602011-10-25 13:32:58 -07001018 if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) {
1019 fb_num = MINOR(file->f_dentry->d_inode->i_rdev);
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -07001020 if (get_fb_phys_info(start, len, fb_num,
1021 ROTATOR_SUBSYSTEM_ID)) {
1022 pr_err("get_fb_phys_info() failed\n");
Naseer Ahmed18018602011-10-25 13:32:58 -07001023 ret = -1;
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -07001024 } else {
Naseer Ahmed18018602011-10-25 13:32:58 -07001025 *p_file = file;
Kuogee Hsiehbeaa9352012-02-24 16:59:26 -08001026 *p_need = put_needed;
1027 }
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -07001028 } else {
1029 pr_err("invalid FB_MAJOR failed\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001030 ret = -1;
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -07001031 }
Naseer Ahmed18018602011-10-25 13:32:58 -07001032 if (ret)
1033 fput_light(file, put_needed);
1034 return ret;
1035 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001036#endif
Naseer Ahmed18018602011-10-25 13:32:58 -07001037
1038#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
Ravishangar Kalyanam1eca3522012-05-31 18:02:24 -07001039 return msm_rotator_iommu_map_buf(fbd->memory_id, domain, start,
1040 len, p_ihdl, secure);
Naseer Ahmed18018602011-10-25 13:32:58 -07001041#endif
1042#ifdef CONFIG_ANDROID_PMEM
1043 if (!get_pmem_file(fbd->memory_id, start, &vstart, len, p_file))
1044 return 0;
1045 else
1046 return -ENOMEM;
1047#endif
1048
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001049}
1050
Olav Hauganef95ae32012-05-15 09:50:30 -07001051static void put_img(struct file *p_file, struct ion_handle *p_ihdl,
Ravishangar Kalyanam1eca3522012-05-31 18:02:24 -07001052 int domain, unsigned int secure)
Naseer Ahmed18018602011-10-25 13:32:58 -07001053{
1054#ifdef CONFIG_ANDROID_PMEM
1055 if (p_file != NULL)
1056 put_pmem_file(p_file);
1057#endif
Olav Hauganef95ae32012-05-15 09:50:30 -07001058
Naseer Ahmed18018602011-10-25 13:32:58 -07001059#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -07001060 if (!IS_ERR_OR_NULL(p_ihdl)) {
1061 pr_debug("%s(): p_ihdl %p\n", __func__, p_ihdl);
Ravishangar Kalyanam1eca3522012-05-31 18:02:24 -07001062 if (rot_iommu_split_domain) {
1063 if (!secure)
1064 ion_unmap_iommu(msm_rotator_dev->client,
1065 p_ihdl, domain, GEN_POOL);
1066 } else {
1067 ion_unmap_iommu(msm_rotator_dev->client,
1068 p_ihdl, ROTATOR_SRC_DOMAIN, GEN_POOL);
1069 }
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -07001070
Naseer Ahmed18018602011-10-25 13:32:58 -07001071 ion_free(msm_rotator_dev->client, p_ihdl);
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -07001072 }
Naseer Ahmed18018602011-10-25 13:32:58 -07001073#endif
1074}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001075static int msm_rotator_do_rotate(unsigned long arg)
1076{
Naseer Ahmed18018602011-10-25 13:32:58 -07001077 unsigned int status, format;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001078 struct msm_rotator_data_info info;
1079 unsigned int in_paddr, out_paddr;
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001080 unsigned long src_len, dst_len;
Naseer Ahmed18018602011-10-25 13:32:58 -07001081 int use_imem = 0, rc = 0, s;
1082 struct file *srcp0_file = NULL, *dstp0_file = NULL;
1083 struct file *srcp1_file = NULL, *dstp1_file = NULL;
1084 struct ion_handle *srcp0_ihdl = NULL, *dstp0_ihdl = NULL;
1085 struct ion_handle *srcp1_ihdl = NULL, *dstp1_ihdl = NULL;
Kuogee Hsiehbeaa9352012-02-24 16:59:26 -08001086 int ps0_need, p_need;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001087 unsigned int in_chroma_paddr = 0, out_chroma_paddr = 0;
Mayank Chopra27147a72012-07-17 13:57:22 +05301088 unsigned int in_chroma2_paddr = 0, out_chroma2_paddr = 0;
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001089 struct msm_rotator_img_info *img_info;
1090 struct msm_rotator_mem_planes src_planes, dst_planes;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001091
1092 if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
1093 return -EFAULT;
1094
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001095 mutex_lock(&msm_rotator_dev->rotator_lock);
1096 for (s = 0; s < MAX_SESSIONS; s++)
Mayank Chopra27147a72012-07-17 13:57:22 +05301097 if ((msm_rotator_dev->rot_session[s] != NULL) &&
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001098 (info.session_id ==
Mayank Chopra27147a72012-07-17 13:57:22 +05301099 (unsigned int)msm_rotator_dev->rot_session[s]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001100 ))
1101 break;
1102
1103 if (s == MAX_SESSIONS) {
Ravishangar Kalyanam30cf3eb2012-04-13 18:10:26 -07001104 pr_err("%s() : Attempt to use invalid session_id %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001105 __func__, s);
1106 rc = -EINVAL;
Kuogee Hsiehb98fad42013-03-27 17:41:49 -07001107 mutex_unlock(&msm_rotator_dev->rotator_lock);
1108 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001109 }
1110
Mayank Chopra27147a72012-07-17 13:57:22 +05301111 img_info = &(msm_rotator_dev->rot_session[s]->img_info);
1112 if (img_info->enable == 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001113 dev_dbg(msm_rotator_dev->device,
Mayank Chopra27147a72012-07-17 13:57:22 +05301114 "%s() : Session_id %d not enabled\n", __func__, s);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001115 rc = -EINVAL;
Kuogee Hsiehb98fad42013-03-27 17:41:49 -07001116 mutex_unlock(&msm_rotator_dev->rotator_lock);
1117 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001118 }
1119
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001120 if (msm_rotator_get_plane_sizes(img_info->src.format,
1121 img_info->src.width,
1122 img_info->src.height,
1123 &src_planes)) {
1124 pr_err("%s: invalid src format\n", __func__);
1125 rc = -EINVAL;
Kuogee Hsiehb98fad42013-03-27 17:41:49 -07001126 mutex_unlock(&msm_rotator_dev->rotator_lock);
1127 return rc;
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001128 }
1129 if (msm_rotator_get_plane_sizes(img_info->dst.format,
1130 img_info->dst.width,
1131 img_info->dst.height,
1132 &dst_planes)) {
1133 pr_err("%s: invalid dst format\n", __func__);
1134 rc = -EINVAL;
Kuogee Hsiehb98fad42013-03-27 17:41:49 -07001135 mutex_unlock(&msm_rotator_dev->rotator_lock);
1136 return rc;
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001137 }
1138
Ravishangar Kalyanam1eca3522012-05-31 18:02:24 -07001139 rc = get_img(&info.src, ROTATOR_SRC_DOMAIN, (unsigned long *)&in_paddr,
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -07001140 (unsigned long *)&src_len, &srcp0_file, &ps0_need,
Ravishangar Kalyanam1eca3522012-05-31 18:02:24 -07001141 &srcp0_ihdl, 0);
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001142 if (rc) {
1143 pr_err("%s: in get_img() failed id=0x%08x\n",
1144 DRIVER_NAME, info.src.memory_id);
1145 goto do_rotate_unlock_mutex;
1146 }
1147
Ravishangar Kalyanam1eca3522012-05-31 18:02:24 -07001148 rc = get_img(&info.dst, ROTATOR_DST_DOMAIN, (unsigned long *)&out_paddr,
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -07001149 (unsigned long *)&dst_len, &dstp0_file, &p_need,
Ravishangar Kalyanam1eca3522012-05-31 18:02:24 -07001150 &dstp0_ihdl, img_info->secure);
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001151 if (rc) {
1152 pr_err("%s: out get_img() failed id=0x%08x\n",
1153 DRIVER_NAME, info.dst.memory_id);
1154 goto do_rotate_unlock_mutex;
1155 }
1156
Mayank Chopra27147a72012-07-17 13:57:22 +05301157 format = img_info->src.format;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001158 if (((info.version_key & VERSION_KEY_MASK) == 0xA5B4C300) &&
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001159 ((info.version_key & ~VERSION_KEY_MASK) > 0) &&
1160 (src_planes.num_planes == 2)) {
1161 if (checkoffset(info.src.offset,
1162 src_planes.plane_size[0],
1163 src_len)) {
1164 pr_err("%s: invalid src buffer (len=%lu offset=%x)\n",
1165 __func__, src_len, info.src.offset);
1166 rc = -ERANGE;
1167 goto do_rotate_unlock_mutex;
1168 }
1169 if (checkoffset(info.dst.offset,
1170 dst_planes.plane_size[0],
1171 dst_len)) {
1172 pr_err("%s: invalid dst buffer (len=%lu offset=%x)\n",
1173 __func__, dst_len, info.dst.offset);
1174 rc = -ERANGE;
1175 goto do_rotate_unlock_mutex;
1176 }
1177
Ravishangar Kalyanam1eca3522012-05-31 18:02:24 -07001178 rc = get_img(&info.src_chroma, ROTATOR_SRC_DOMAIN,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001179 (unsigned long *)&in_chroma_paddr,
Kuogee Hsiehbeaa9352012-02-24 16:59:26 -08001180 (unsigned long *)&src_len, &srcp1_file, &p_need,
Ravishangar Kalyanam1eca3522012-05-31 18:02:24 -07001181 &srcp1_ihdl, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001182 if (rc) {
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001183 pr_err("%s: in chroma get_img() failed id=0x%08x\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001184 DRIVER_NAME, info.src_chroma.memory_id);
1185 goto do_rotate_unlock_mutex;
1186 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001187
Ravishangar Kalyanam1eca3522012-05-31 18:02:24 -07001188 rc = get_img(&info.dst_chroma, ROTATOR_DST_DOMAIN,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001189 (unsigned long *)&out_chroma_paddr,
Kuogee Hsiehbeaa9352012-02-24 16:59:26 -08001190 (unsigned long *)&dst_len, &dstp1_file, &p_need,
Ravishangar Kalyanam1eca3522012-05-31 18:02:24 -07001191 &dstp1_ihdl, img_info->secure);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001192 if (rc) {
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001193 pr_err("%s: out chroma get_img() failed id=0x%08x\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001194 DRIVER_NAME, info.dst_chroma.memory_id);
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001195 goto do_rotate_unlock_mutex;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001196 }
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001197
1198 if (checkoffset(info.src_chroma.offset,
1199 src_planes.plane_size[1],
1200 src_len)) {
1201 pr_err("%s: invalid chr src buf len=%lu offset=%x\n",
1202 __func__, src_len, info.src_chroma.offset);
1203 rc = -ERANGE;
1204 goto do_rotate_unlock_mutex;
1205 }
1206
1207 if (checkoffset(info.dst_chroma.offset,
1208 src_planes.plane_size[1],
1209 dst_len)) {
1210 pr_err("%s: invalid chr dst buf len=%lu offset=%x\n",
1211 __func__, dst_len, info.dst_chroma.offset);
1212 rc = -ERANGE;
1213 goto do_rotate_unlock_mutex;
1214 }
1215
1216 in_chroma_paddr += info.src_chroma.offset;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001217 out_chroma_paddr += info.dst_chroma.offset;
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001218 } else {
1219 if (checkoffset(info.src.offset,
1220 src_planes.total_size,
1221 src_len)) {
1222 pr_err("%s: invalid src buffer (len=%lu offset=%x)\n",
1223 __func__, src_len, info.src.offset);
1224 rc = -ERANGE;
1225 goto do_rotate_unlock_mutex;
1226 }
1227 if (checkoffset(info.dst.offset,
1228 dst_planes.total_size,
1229 dst_len)) {
1230 pr_err("%s: invalid dst buffer (len=%lu offset=%x)\n",
1231 __func__, dst_len, info.dst.offset);
1232 rc = -ERANGE;
1233 goto do_rotate_unlock_mutex;
1234 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001235 }
1236
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001237 in_paddr += info.src.offset;
1238 out_paddr += info.dst.offset;
1239
1240 if (!in_chroma_paddr && src_planes.num_planes >= 2)
1241 in_chroma_paddr = in_paddr + src_planes.plane_size[0];
1242 if (!out_chroma_paddr && dst_planes.num_planes >= 2)
1243 out_chroma_paddr = out_paddr + dst_planes.plane_size[0];
1244 if (src_planes.num_planes >= 3)
1245 in_chroma2_paddr = in_chroma_paddr + src_planes.plane_size[1];
Mayank Chopra27147a72012-07-17 13:57:22 +05301246 if (dst_planes.num_planes >= 3)
1247 out_chroma2_paddr = out_chroma_paddr + dst_planes.plane_size[1];
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001248
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001249 cancel_delayed_work(&msm_rotator_dev->rot_clk_work);
1250 if (msm_rotator_dev->rot_clk_state != CLK_EN) {
1251 enable_rot_clks();
1252 msm_rotator_dev->rot_clk_state = CLK_EN;
1253 }
1254 enable_irq(msm_rotator_dev->irq);
1255
1256#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
1257 use_imem = msm_rotator_imem_allocate(ROTATOR_REQUEST);
1258#else
1259 use_imem = 0;
1260#endif
1261 /*
1262 * workaround for a hardware bug. rotator hardware hangs when we
1263 * use write burst beat size 16 on 128X128 tile fetch mode. As a
1264 * temporary fix use 0x42 for BURST_SIZE when imem used.
1265 */
1266 if (use_imem)
1267 iowrite32(0x42, MSM_ROTATOR_MAX_BURST_SIZE);
1268
Mayank Chopra27147a72012-07-17 13:57:22 +05301269 iowrite32(((img_info->src_rect.h & 0x1fff)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001270 << 16) |
Mayank Chopra27147a72012-07-17 13:57:22 +05301271 (img_info->src_rect.w & 0x1fff),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001272 MSM_ROTATOR_SRC_SIZE);
Mayank Chopra27147a72012-07-17 13:57:22 +05301273 iowrite32(((img_info->src_rect.y & 0x1fff)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001274 << 16) |
Mayank Chopra27147a72012-07-17 13:57:22 +05301275 (img_info->src_rect.x & 0x1fff),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001276 MSM_ROTATOR_SRC_XY);
Mayank Chopra27147a72012-07-17 13:57:22 +05301277 iowrite32(((img_info->src.height & 0x1fff)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001278 << 16) |
Mayank Chopra27147a72012-07-17 13:57:22 +05301279 (img_info->src.width & 0x1fff),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001280 MSM_ROTATOR_SRC_IMAGE_SIZE);
1281
1282 switch (format) {
1283 case MDP_RGB_565:
1284 case MDP_BGR_565:
1285 case MDP_RGB_888:
1286 case MDP_ARGB_8888:
1287 case MDP_RGBA_8888:
1288 case MDP_XRGB_8888:
1289 case MDP_BGRA_8888:
1290 case MDP_RGBX_8888:
Adrian Salido-Morenoeeb06c72011-08-15 10:41:35 -07001291 case MDP_YCBCR_H1V1:
1292 case MDP_YCRCB_H1V1:
Mayank Chopra27147a72012-07-17 13:57:22 +05301293 rc = msm_rotator_rgb_types(img_info,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001294 in_paddr, out_paddr,
1295 use_imem,
1296 msm_rotator_dev->last_session_idx
1297 != s);
1298 break;
1299 case MDP_Y_CBCR_H2V2:
1300 case MDP_Y_CRCB_H2V2:
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -07001301 case MDP_Y_CB_CR_H2V2:
1302 case MDP_Y_CR_CB_H2V2:
Pradeep Jilagam9b4a6be2011-10-03 17:19:20 +05301303 case MDP_Y_CR_CB_GH2V2:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001304 case MDP_Y_CRCB_H2V2_TILE:
1305 case MDP_Y_CBCR_H2V2_TILE:
Mayank Chopra27147a72012-07-17 13:57:22 +05301306 rc = msm_rotator_ycxcx_h2v2(img_info,
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001307 in_paddr, out_paddr, use_imem,
1308 msm_rotator_dev->last_session_idx
1309 != s,
1310 in_chroma_paddr,
1311 out_chroma_paddr,
Mayank Chopra27147a72012-07-17 13:57:22 +05301312 in_chroma2_paddr,
1313 out_chroma2_paddr);
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001314 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001315 case MDP_Y_CBCR_H2V1:
1316 case MDP_Y_CRCB_H2V1:
Mayank Chopra27147a72012-07-17 13:57:22 +05301317 rc = msm_rotator_ycxcx_h2v1(img_info,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001318 in_paddr, out_paddr, use_imem,
1319 msm_rotator_dev->last_session_idx
1320 != s,
1321 in_chroma_paddr,
1322 out_chroma_paddr);
1323 break;
1324 case MDP_YCRYCB_H2V1:
Mayank Chopra27147a72012-07-17 13:57:22 +05301325 rc = msm_rotator_ycrycb(img_info,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001326 in_paddr, out_paddr, use_imem,
Mayank Chopra732dcd62012-01-09 20:53:39 +05301327 msm_rotator_dev->last_session_idx != s,
1328 out_chroma_paddr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001329 break;
1330 default:
1331 rc = -EINVAL;
Ravishangar Kalyanam30cf3eb2012-04-13 18:10:26 -07001332 pr_err("%s(): Unsupported format %u\n", __func__, format);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001333 goto do_rotate_exit;
1334 }
1335
1336 if (rc != 0) {
1337 msm_rotator_dev->last_session_idx = INVALID_SESSION;
Ravishangar Kalyanam30cf3eb2012-04-13 18:10:26 -07001338 pr_err("%s(): Invalid session error\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001339 goto do_rotate_exit;
1340 }
1341
1342 iowrite32(3, MSM_ROTATOR_INTR_ENABLE);
1343
1344 msm_rotator_dev->processing = 1;
1345 iowrite32(0x1, MSM_ROTATOR_START);
1346
1347 wait_event(msm_rotator_dev->wq,
1348 (msm_rotator_dev->processing == 0));
1349 status = (unsigned char)ioread32(MSM_ROTATOR_INTR_STATUS);
Ravishangar Kalyanam30cf3eb2012-04-13 18:10:26 -07001350 if ((status & 0x03) != 0x01) {
1351 pr_err("%s(): AXI Bus Error, issuing SW_RESET\n", __func__);
1352 iowrite32(0x1, MSM_ROTATOR_SW_RESET);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001353 rc = -EFAULT;
Ravishangar Kalyanam30cf3eb2012-04-13 18:10:26 -07001354 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001355 iowrite32(0, MSM_ROTATOR_INTR_ENABLE);
1356 iowrite32(3, MSM_ROTATOR_INTR_CLEAR);
1357
1358do_rotate_exit:
1359 disable_irq(msm_rotator_dev->irq);
1360#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
1361 msm_rotator_imem_free(ROTATOR_REQUEST);
1362#endif
1363 schedule_delayed_work(&msm_rotator_dev->rot_clk_work, HZ);
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001364do_rotate_unlock_mutex:
Ravishangar Kalyanam1eca3522012-05-31 18:02:24 -07001365 put_img(dstp1_file, dstp1_ihdl, ROTATOR_DST_DOMAIN,
Mayank Chopra27147a72012-07-17 13:57:22 +05301366 msm_rotator_dev->rot_session[s]->img_info.secure);
Ravishangar Kalyanam1eca3522012-05-31 18:02:24 -07001367 put_img(srcp1_file, srcp1_ihdl, ROTATOR_SRC_DOMAIN, 0);
1368 put_img(dstp0_file, dstp0_ihdl, ROTATOR_DST_DOMAIN,
Mayank Chopra27147a72012-07-17 13:57:22 +05301369 msm_rotator_dev->rot_session[s]->img_info.secure);
Kuogee Hsiehbeaa9352012-02-24 16:59:26 -08001370
1371 /* only source may use frame buffer */
1372 if (info.src.flags & MDP_MEMORY_ID_TYPE_FB)
1373 fput_light(srcp0_file, ps0_need);
1374 else
Ravishangar Kalyanam1eca3522012-05-31 18:02:24 -07001375 put_img(srcp0_file, srcp0_ihdl, ROTATOR_SRC_DOMAIN, 0);
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001376 mutex_unlock(&msm_rotator_dev->rotator_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001377 dev_dbg(msm_rotator_dev->device, "%s() returning rc = %d\n",
1378 __func__, rc);
1379 return rc;
1380}
1381
Nagamalleswararao Ganjie1a9f872011-11-06 23:13:32 -08001382static void msm_rotator_set_perf_level(u32 wh, u32 is_rgb)
1383{
1384 u32 perf_level;
1385
1386 if (is_rgb)
1387 perf_level = 1;
1388 else if (wh <= (640 * 480))
1389 perf_level = 2;
1390 else if (wh <= (736 * 1280))
1391 perf_level = 3;
1392 else
1393 perf_level = 4;
1394
1395#ifdef CONFIG_MSM_BUS_SCALING
1396 msm_bus_scale_client_update_request(msm_rotator_dev->bus_client_handle,
1397 perf_level);
1398#endif
1399
1400}
1401
Ken Zhangfebeabd2012-12-17 10:35:15 -05001402static int rot_enable_iommu_clocks(struct msm_rotator_dev *rot_dev)
1403{
1404 int ret = 0, i;
1405 if (rot_dev->mmu_clk_on)
1406 return 0;
1407 for (i = 0; i < ARRAY_SIZE(rot_mmu_clks); i++) {
1408 rot_mmu_clks[i].mmu_clk = clk_get(&msm_rotator_dev->pdev->dev,
1409 rot_mmu_clks[i].mmu_clk_name);
1410 if (IS_ERR(rot_mmu_clks[i].mmu_clk)) {
1411 pr_err(" %s: Get failed for clk %s", __func__,
1412 rot_mmu_clks[i].mmu_clk_name);
1413 ret = PTR_ERR(rot_mmu_clks[i].mmu_clk);
1414 break;
1415 }
1416 ret = clk_prepare_enable(rot_mmu_clks[i].mmu_clk);
1417 if (ret) {
1418 clk_put(rot_mmu_clks[i].mmu_clk);
1419 rot_mmu_clks[i].mmu_clk = NULL;
1420 }
1421 }
1422 if (ret) {
1423 for (i--; i >= 0; i--) {
1424 clk_disable_unprepare(rot_mmu_clks[i].mmu_clk);
1425 clk_put(rot_mmu_clks[i].mmu_clk);
1426 rot_mmu_clks[i].mmu_clk = NULL;
1427 }
1428 } else {
1429 rot_dev->mmu_clk_on = 1;
1430 }
1431 return ret;
1432}
1433
1434static int rot_disable_iommu_clocks(struct msm_rotator_dev *rot_dev)
1435{
1436 int i;
1437 if (!rot_dev->mmu_clk_on)
1438 return 0;
1439 for (i = 0; i < ARRAY_SIZE(rot_mmu_clks); i++) {
1440 clk_disable_unprepare(rot_mmu_clks[i].mmu_clk);
1441 clk_put(rot_mmu_clks[i].mmu_clk);
1442 rot_mmu_clks[i].mmu_clk = NULL;
1443 }
1444 rot_dev->mmu_clk_on = 0;
1445 return 0;
1446}
1447
1448static int map_sec_resource(struct msm_rotator_dev *rot_dev)
1449{
1450 int ret = 0;
1451 if (rot_dev->sec_mapped)
1452 return 0;
1453
1454 ret = rot_enable_iommu_clocks(rot_dev);
1455 if (ret) {
1456 pr_err("IOMMU clock enabled failed while open");
1457 return ret;
1458 }
1459 ret = msm_ion_secure_heap(ION_HEAP(ION_CP_MM_HEAP_ID));
1460 if (ret)
1461 pr_err("ION heap secure failed heap id %d ret %d\n",
1462 ION_CP_MM_HEAP_ID, ret);
1463 else
1464 rot_dev->sec_mapped = 1;
1465 rot_disable_iommu_clocks(rot_dev);
1466 return ret;
1467}
1468
1469static int unmap_sec_resource(struct msm_rotator_dev *rot_dev)
1470{
1471 int ret = 0;
1472 ret = rot_enable_iommu_clocks(rot_dev);
1473 if (ret) {
1474 pr_err("IOMMU clock enabled failed while close\n");
1475 return ret;
1476 }
1477 msm_ion_unsecure_heap(ION_HEAP(ION_CP_MM_HEAP_ID));
1478 rot_dev->sec_mapped = 0;
1479 rot_disable_iommu_clocks(rot_dev);
1480 return ret;
1481}
1482
Adrian Salido-Moreno5737db12012-04-02 15:22:08 -07001483static int msm_rotator_start(unsigned long arg,
1484 struct msm_rotator_fd_info *fd_info)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001485{
1486 struct msm_rotator_img_info info;
Mayank Chopra27147a72012-07-17 13:57:22 +05301487 struct msm_rotator_session *rot_session = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001488 int rc = 0;
Nagamalleswararao Ganjie1a9f872011-11-06 23:13:32 -08001489 int s, is_rgb = 0;
Mayank Chopra27147a72012-07-17 13:57:22 +05301490 int first_free_idx = INVALID_SESSION;
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001491 unsigned int dst_w, dst_h;
Mayank Chopra27147a72012-07-17 13:57:22 +05301492 unsigned int is_planar420 = 0;
1493 int fast_yuv_en = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001494
1495 if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
1496 return -EFAULT;
1497
1498 if ((info.rotations > MSM_ROTATOR_MAX_ROT) ||
1499 (info.src.height > MSM_ROTATOR_MAX_H) ||
1500 (info.src.width > MSM_ROTATOR_MAX_W) ||
1501 (info.dst.height > MSM_ROTATOR_MAX_H) ||
1502 (info.dst.width > MSM_ROTATOR_MAX_W) ||
Adrian Salido-Moreno67273e52011-10-21 19:04:18 -07001503 (info.downscale_ratio > MAX_DOWNSCALE_RATIO)) {
1504 pr_err("%s: Invalid parameters\n", __func__);
1505 return -EINVAL;
1506 }
1507
1508 if (info.rotations & MDP_ROT_90) {
1509 dst_w = info.src_rect.h >> info.downscale_ratio;
1510 dst_h = info.src_rect.w >> info.downscale_ratio;
1511 } else {
1512 dst_w = info.src_rect.w >> info.downscale_ratio;
1513 dst_h = info.src_rect.h >> info.downscale_ratio;
1514 }
1515
1516 if (checkoffset(info.src_rect.x, info.src_rect.w, info.src.width) ||
Adrian Salido-Moreno88411862011-09-20 18:54:03 -07001517 checkoffset(info.src_rect.y, info.src_rect.h, info.src.height) ||
1518 checkoffset(info.dst_x, dst_w, info.dst.width) ||
Adrian Salido-Moreno67273e52011-10-21 19:04:18 -07001519 checkoffset(info.dst_y, dst_h, info.dst.height)) {
1520 pr_err("%s: Invalid src or dst rect\n", __func__);
1521 return -ERANGE;
1522 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001523
1524 switch (info.src.format) {
Mayank Chopra27147a72012-07-17 13:57:22 +05301525 case MDP_Y_CB_CR_H2V2:
1526 case MDP_Y_CR_CB_H2V2:
1527 case MDP_Y_CR_CB_GH2V2:
1528 is_planar420 = 1;
1529 case MDP_Y_CBCR_H2V2:
1530 case MDP_Y_CRCB_H2V2:
1531 case MDP_Y_CRCB_H2V2_TILE:
1532 case MDP_Y_CBCR_H2V2_TILE:
1533 if (rotator_hw_revision >= ROTATOR_REVISION_V2 &&
1534 !(info.downscale_ratio &&
1535 (info.rotations & MDP_ROT_90)))
1536 fast_yuv_en = !fast_yuv_invalid_size_checker(
1537 info.rotations,
1538 info.src.width,
1539 dst_w,
1540 dst_h,
1541 dst_w,
1542 is_planar420);
1543 break;
1544 default:
1545 fast_yuv_en = 0;
1546 }
1547
1548 switch (info.src.format) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001549 case MDP_RGB_565:
1550 case MDP_BGR_565:
1551 case MDP_RGB_888:
1552 case MDP_ARGB_8888:
1553 case MDP_RGBA_8888:
1554 case MDP_XRGB_8888:
1555 case MDP_RGBX_8888:
1556 case MDP_BGRA_8888:
Nagamalleswararao Ganjie1a9f872011-11-06 23:13:32 -08001557 is_rgb = 1;
Mayank Chopra012a8e72012-04-11 10:41:13 +05301558 info.dst.format = info.src.format;
Nagamalleswararao Ganjie1a9f872011-11-06 23:13:32 -08001559 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001560 case MDP_Y_CBCR_H2V2:
1561 case MDP_Y_CRCB_H2V2:
Mayank Chopra012a8e72012-04-11 10:41:13 +05301562 case MDP_Y_CBCR_H2V1:
1563 case MDP_Y_CRCB_H2V1:
Adrian Salido-Morenoeeb06c72011-08-15 10:41:35 -07001564 case MDP_YCBCR_H1V1:
1565 case MDP_YCRCB_H1V1:
Mayank Chopra012a8e72012-04-11 10:41:13 +05301566 info.dst.format = info.src.format;
1567 break;
1568 case MDP_YCRYCB_H2V1:
Mayank Chopra797bdb72012-03-03 06:29:40 +05301569 if (info.rotations & MDP_ROT_90)
1570 info.dst.format = MDP_Y_CRCB_H1V2;
1571 else
1572 info.dst.format = MDP_Y_CRCB_H2V1;
Mayank Chopra012a8e72012-04-11 10:41:13 +05301573 break;
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -07001574 case MDP_Y_CB_CR_H2V2:
Mayank Chopra27147a72012-07-17 13:57:22 +05301575 if (fast_yuv_en) {
1576 info.dst.format = info.src.format;
1577 break;
1578 }
Mayank Chopra012a8e72012-04-11 10:41:13 +05301579 case MDP_Y_CBCR_H2V2_TILE:
1580 info.dst.format = MDP_Y_CBCR_H2V2;
1581 break;
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -07001582 case MDP_Y_CR_CB_H2V2:
Pradeep Jilagam9b4a6be2011-10-03 17:19:20 +05301583 case MDP_Y_CR_CB_GH2V2:
Mayank Chopra27147a72012-07-17 13:57:22 +05301584 if (fast_yuv_en) {
1585 info.dst.format = info.src.format;
1586 break;
1587 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001588 case MDP_Y_CRCB_H2V2_TILE:
Mayank Chopra012a8e72012-04-11 10:41:13 +05301589 info.dst.format = MDP_Y_CRCB_H2V2;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001590 break;
1591 default:
1592 return -EINVAL;
1593 }
1594
1595 mutex_lock(&msm_rotator_dev->rotator_lock);
Nagamalleswararao Ganjie1a9f872011-11-06 23:13:32 -08001596
1597 msm_rotator_set_perf_level((info.src.width*info.src.height), is_rgb);
1598
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001599 for (s = 0; s < MAX_SESSIONS; s++) {
Mayank Chopra27147a72012-07-17 13:57:22 +05301600 if ((msm_rotator_dev->rot_session[s] != NULL) &&
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001601 (info.session_id ==
Mayank Chopra27147a72012-07-17 13:57:22 +05301602 (unsigned int)msm_rotator_dev->rot_session[s]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001603 )) {
Mayank Chopra27147a72012-07-17 13:57:22 +05301604 rot_session = msm_rotator_dev->rot_session[s];
1605 rot_session->img_info = info;
1606 rot_session->fd_info = *fd_info;
1607 rot_session->fast_yuv_enable = fast_yuv_en;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001608
1609 if (msm_rotator_dev->last_session_idx == s)
1610 msm_rotator_dev->last_session_idx =
1611 INVALID_SESSION;
1612 break;
1613 }
1614
Mayank Chopra27147a72012-07-17 13:57:22 +05301615 if ((msm_rotator_dev->rot_session[s] == NULL) &&
1616 (first_free_idx == INVALID_SESSION))
1617 first_free_idx = s;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001618 }
1619
Mayank Chopra27147a72012-07-17 13:57:22 +05301620 if ((s == MAX_SESSIONS) && (first_free_idx != INVALID_SESSION)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001621 /* allocate a session id */
Mayank Chopra27147a72012-07-17 13:57:22 +05301622 msm_rotator_dev->rot_session[first_free_idx] =
1623 kzalloc(sizeof(struct msm_rotator_session),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001624 GFP_KERNEL);
Mayank Chopra27147a72012-07-17 13:57:22 +05301625 if (!msm_rotator_dev->rot_session[first_free_idx]) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001626 printk(KERN_ERR "%s : unable to alloc mem\n",
1627 __func__);
1628 rc = -ENOMEM;
1629 goto rotator_start_exit;
1630 }
1631 info.session_id = (unsigned int)
Mayank Chopra27147a72012-07-17 13:57:22 +05301632 msm_rotator_dev->rot_session[first_free_idx];
1633 rot_session = msm_rotator_dev->rot_session[first_free_idx];
1634
1635 rot_session->img_info = info;
1636 rot_session->fd_info = *fd_info;
1637 rot_session->fast_yuv_enable = fast_yuv_en;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001638 } else if (s == MAX_SESSIONS) {
1639 dev_dbg(msm_rotator_dev->device, "%s: all sessions in use\n",
1640 __func__);
1641 rc = -EBUSY;
1642 }
1643
Adrian Salido-Morenoc5639952012-04-20 19:07:58 -07001644 if (rc == 0 && copy_to_user((void __user *)arg, &info, sizeof(info)))
1645 rc = -EFAULT;
Ken Zhangfebeabd2012-12-17 10:35:15 -05001646 if ((rc == 0) && (info.secure))
1647 map_sec_resource(msm_rotator_dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001648rotator_start_exit:
1649 mutex_unlock(&msm_rotator_dev->rotator_lock);
1650
1651 return rc;
1652}
1653
1654static int msm_rotator_finish(unsigned long arg)
1655{
1656 int rc = 0;
1657 int s;
1658 unsigned int session_id;
1659
1660 if (copy_from_user(&session_id, (void __user *)arg, sizeof(s)))
1661 return -EFAULT;
1662
1663 mutex_lock(&msm_rotator_dev->rotator_lock);
1664 for (s = 0; s < MAX_SESSIONS; s++) {
Mayank Chopra27147a72012-07-17 13:57:22 +05301665 if ((msm_rotator_dev->rot_session[s] != NULL) &&
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001666 (session_id ==
Mayank Chopra27147a72012-07-17 13:57:22 +05301667 (unsigned int)msm_rotator_dev->rot_session[s])) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001668 if (msm_rotator_dev->last_session_idx == s)
1669 msm_rotator_dev->last_session_idx =
1670 INVALID_SESSION;
Mayank Chopra27147a72012-07-17 13:57:22 +05301671 kfree(msm_rotator_dev->rot_session[s]);
1672 msm_rotator_dev->rot_session[s] = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001673 break;
1674 }
1675 }
1676
1677 if (s == MAX_SESSIONS)
1678 rc = -EINVAL;
Nagamalleswararao Ganjie1a9f872011-11-06 23:13:32 -08001679#ifdef CONFIG_MSM_BUS_SCALING
1680 msm_bus_scale_client_update_request(msm_rotator_dev->bus_client_handle,
1681 0);
1682#endif
Ken Zhangfebeabd2012-12-17 10:35:15 -05001683 if (msm_rotator_dev->sec_mapped)
1684 unmap_sec_resource(msm_rotator_dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001685 mutex_unlock(&msm_rotator_dev->rotator_lock);
1686 return rc;
1687}
1688
1689static int
1690msm_rotator_open(struct inode *inode, struct file *filp)
1691{
Adrian Salido-Moreno5737db12012-04-02 15:22:08 -07001692 struct msm_rotator_fd_info *tmp, *fd_info = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001693 int i;
1694
1695 if (filp->private_data)
1696 return -EBUSY;
1697
1698 mutex_lock(&msm_rotator_dev->rotator_lock);
Adrian Salido-Moreno5737db12012-04-02 15:22:08 -07001699 for (i = 0; i < MAX_SESSIONS; i++) {
Mayank Chopra27147a72012-07-17 13:57:22 +05301700 if (msm_rotator_dev->rot_session[i] == NULL)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001701 break;
1702 }
Adrian Salido-Moreno5737db12012-04-02 15:22:08 -07001703
1704 if (i == MAX_SESSIONS) {
1705 mutex_unlock(&msm_rotator_dev->rotator_lock);
1706 return -EBUSY;
1707 }
1708
1709 list_for_each_entry(tmp, &msm_rotator_dev->fd_list, list) {
1710 if (tmp->pid == current->pid) {
1711 fd_info = tmp;
1712 break;
1713 }
1714 }
1715
1716 if (!fd_info) {
1717 fd_info = kzalloc(sizeof(*fd_info), GFP_KERNEL);
1718 if (!fd_info) {
1719 mutex_unlock(&msm_rotator_dev->rotator_lock);
1720 pr_err("%s: insufficient memory to alloc resources\n",
1721 __func__);
1722 return -ENOMEM;
1723 }
1724 list_add(&fd_info->list, &msm_rotator_dev->fd_list);
1725 fd_info->pid = current->pid;
1726 }
1727 fd_info->ref_cnt++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001728 mutex_unlock(&msm_rotator_dev->rotator_lock);
1729
Adrian Salido-Moreno5737db12012-04-02 15:22:08 -07001730 filp->private_data = fd_info;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001731
1732 return 0;
1733}
1734
1735static int
1736msm_rotator_close(struct inode *inode, struct file *filp)
1737{
Adrian Salido-Moreno5737db12012-04-02 15:22:08 -07001738 struct msm_rotator_fd_info *fd_info;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001739 int s;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001740
Adrian Salido-Moreno5737db12012-04-02 15:22:08 -07001741 fd_info = (struct msm_rotator_fd_info *)filp->private_data;
1742
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001743 mutex_lock(&msm_rotator_dev->rotator_lock);
Adrian Salido-Moreno5737db12012-04-02 15:22:08 -07001744 if (--fd_info->ref_cnt > 0) {
1745 mutex_unlock(&msm_rotator_dev->rotator_lock);
1746 return 0;
1747 }
1748
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001749 for (s = 0; s < MAX_SESSIONS; s++) {
Mayank Chopra27147a72012-07-17 13:57:22 +05301750 if (msm_rotator_dev->rot_session[s] != NULL &&
1751 &(msm_rotator_dev->rot_session[s]->fd_info) == fd_info) {
Adrian Salido-Moreno5737db12012-04-02 15:22:08 -07001752 pr_debug("%s: freeing rotator session %p (pid %d)\n",
Mayank Chopra27147a72012-07-17 13:57:22 +05301753 __func__, msm_rotator_dev->rot_session[s],
Adrian Salido-Moreno5737db12012-04-02 15:22:08 -07001754 fd_info->pid);
Mayank Chopra27147a72012-07-17 13:57:22 +05301755 kfree(msm_rotator_dev->rot_session[s]);
1756 msm_rotator_dev->rot_session[s] = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001757 if (msm_rotator_dev->last_session_idx == s)
1758 msm_rotator_dev->last_session_idx =
1759 INVALID_SESSION;
1760 }
1761 }
Adrian Salido-Moreno5737db12012-04-02 15:22:08 -07001762 list_del(&fd_info->list);
1763 kfree(fd_info);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001764 mutex_unlock(&msm_rotator_dev->rotator_lock);
1765
1766 return 0;
1767}
1768
1769static long msm_rotator_ioctl(struct file *file, unsigned cmd,
1770 unsigned long arg)
1771{
Adrian Salido-Moreno5737db12012-04-02 15:22:08 -07001772 struct msm_rotator_fd_info *fd_info;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001773
1774 if (_IOC_TYPE(cmd) != MSM_ROTATOR_IOCTL_MAGIC)
1775 return -ENOTTY;
1776
Adrian Salido-Moreno5737db12012-04-02 15:22:08 -07001777 fd_info = (struct msm_rotator_fd_info *)file->private_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001778
1779 switch (cmd) {
1780 case MSM_ROTATOR_IOCTL_START:
Adrian Salido-Moreno5737db12012-04-02 15:22:08 -07001781 return msm_rotator_start(arg, fd_info);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001782 case MSM_ROTATOR_IOCTL_ROTATE:
1783 return msm_rotator_do_rotate(arg);
1784 case MSM_ROTATOR_IOCTL_FINISH:
1785 return msm_rotator_finish(arg);
1786
1787 default:
1788 dev_dbg(msm_rotator_dev->device,
1789 "unexpected IOCTL %d\n", cmd);
1790 return -ENOTTY;
1791 }
1792}
1793
1794static const struct file_operations msm_rotator_fops = {
1795 .owner = THIS_MODULE,
1796 .open = msm_rotator_open,
1797 .release = msm_rotator_close,
1798 .unlocked_ioctl = msm_rotator_ioctl,
1799};
1800
1801static int __devinit msm_rotator_probe(struct platform_device *pdev)
1802{
1803 int rc = 0;
1804 struct resource *res;
1805 struct msm_rotator_platform_data *pdata = NULL;
1806 int i, number_of_clks;
1807 uint32_t ver;
1808
1809 msm_rotator_dev = kzalloc(sizeof(struct msm_rotator_dev), GFP_KERNEL);
1810 if (!msm_rotator_dev) {
1811 printk(KERN_ERR "%s Unable to allocate memory for struct\n",
1812 __func__);
1813 return -ENOMEM;
1814 }
1815 for (i = 0; i < MAX_SESSIONS; i++)
Mayank Chopra27147a72012-07-17 13:57:22 +05301816 msm_rotator_dev->rot_session[i] = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001817 msm_rotator_dev->last_session_idx = INVALID_SESSION;
1818
1819 pdata = pdev->dev.platform_data;
1820 number_of_clks = pdata->number_of_clocks;
Olav Hauganef95ae32012-05-15 09:50:30 -07001821 rot_iommu_split_domain = pdata->rot_iommu_split_domain;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001822
1823 msm_rotator_dev->imem_owner = IMEM_NO_OWNER;
1824 mutex_init(&msm_rotator_dev->imem_lock);
Adrian Salido-Moreno5737db12012-04-02 15:22:08 -07001825 INIT_LIST_HEAD(&msm_rotator_dev->fd_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001826 msm_rotator_dev->imem_clk_state = CLK_DIS;
1827 INIT_DELAYED_WORK(&msm_rotator_dev->imem_clk_work,
1828 msm_rotator_imem_clk_work_f);
1829 msm_rotator_dev->imem_clk = NULL;
1830 msm_rotator_dev->pdev = pdev;
1831
1832 msm_rotator_dev->core_clk = NULL;
1833 msm_rotator_dev->pclk = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001834
Nagamalleswararao Ganjie1a9f872011-11-06 23:13:32 -08001835#ifdef CONFIG_MSM_BUS_SCALING
1836 if (!msm_rotator_dev->bus_client_handle && pdata &&
1837 pdata->bus_scale_table) {
1838 msm_rotator_dev->bus_client_handle =
1839 msm_bus_scale_register_client(
1840 pdata->bus_scale_table);
1841 if (!msm_rotator_dev->bus_client_handle) {
1842 pr_err("%s not able to get bus scale handle\n",
1843 __func__);
1844 }
1845 }
1846#endif
1847
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001848 for (i = 0; i < number_of_clks; i++) {
1849 if (pdata->rotator_clks[i].clk_type == ROTATOR_IMEM_CLK) {
1850 msm_rotator_dev->imem_clk =
1851 clk_get(&msm_rotator_dev->pdev->dev,
1852 pdata->rotator_clks[i].clk_name);
1853 if (IS_ERR(msm_rotator_dev->imem_clk)) {
1854 rc = PTR_ERR(msm_rotator_dev->imem_clk);
1855 msm_rotator_dev->imem_clk = NULL;
1856 printk(KERN_ERR "%s: cannot get imem_clk "
1857 "rc=%d\n", DRIVER_NAME, rc);
1858 goto error_imem_clk;
1859 }
1860 if (pdata->rotator_clks[i].clk_rate)
Matt Wagantall754f2472011-11-08 15:44:00 -08001861 clk_set_rate(msm_rotator_dev->imem_clk,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001862 pdata->rotator_clks[i].clk_rate);
1863 }
1864 if (pdata->rotator_clks[i].clk_type == ROTATOR_PCLK) {
1865 msm_rotator_dev->pclk =
1866 clk_get(&msm_rotator_dev->pdev->dev,
1867 pdata->rotator_clks[i].clk_name);
1868 if (IS_ERR(msm_rotator_dev->pclk)) {
1869 rc = PTR_ERR(msm_rotator_dev->pclk);
1870 msm_rotator_dev->pclk = NULL;
1871 printk(KERN_ERR "%s: cannot get pclk rc=%d\n",
1872 DRIVER_NAME, rc);
1873 goto error_pclk;
1874 }
1875
1876 if (pdata->rotator_clks[i].clk_rate)
Matt Wagantall754f2472011-11-08 15:44:00 -08001877 clk_set_rate(msm_rotator_dev->pclk,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001878 pdata->rotator_clks[i].clk_rate);
1879 }
1880
1881 if (pdata->rotator_clks[i].clk_type == ROTATOR_CORE_CLK) {
1882 msm_rotator_dev->core_clk =
1883 clk_get(&msm_rotator_dev->pdev->dev,
1884 pdata->rotator_clks[i].clk_name);
1885 if (IS_ERR(msm_rotator_dev->core_clk)) {
1886 rc = PTR_ERR(msm_rotator_dev->core_clk);
1887 msm_rotator_dev->core_clk = NULL;
1888 printk(KERN_ERR "%s: cannot get core clk "
1889 "rc=%d\n", DRIVER_NAME, rc);
1890 goto error_core_clk;
1891 }
1892
1893 if (pdata->rotator_clks[i].clk_rate)
Matt Wagantall754f2472011-11-08 15:44:00 -08001894 clk_set_rate(msm_rotator_dev->core_clk,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001895 pdata->rotator_clks[i].clk_rate);
1896 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001897 }
1898
Matt Wagantall316f2fc2012-05-03 20:41:42 -07001899 msm_rotator_dev->regulator = regulator_get(&msm_rotator_dev->pdev->dev,
1900 "vdd");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001901 if (IS_ERR(msm_rotator_dev->regulator))
1902 msm_rotator_dev->regulator = NULL;
1903
1904 msm_rotator_dev->rot_clk_state = CLK_DIS;
1905 INIT_DELAYED_WORK(&msm_rotator_dev->rot_clk_work,
1906 msm_rotator_rot_clk_work_f);
1907
1908 mutex_init(&msm_rotator_dev->rotator_lock);
Naseer Ahmed18018602011-10-25 13:32:58 -07001909#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
1910 msm_rotator_dev->client = msm_ion_client_create(-1, pdev->name);
1911#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001912 platform_set_drvdata(pdev, msm_rotator_dev);
1913
1914
1915 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1916 if (!res) {
1917 printk(KERN_ALERT
1918 "%s: could not get IORESOURCE_MEM\n", DRIVER_NAME);
1919 rc = -ENODEV;
1920 goto error_get_resource;
1921 }
1922 msm_rotator_dev->io_base = ioremap(res->start,
1923 resource_size(res));
1924
1925#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
1926 if (msm_rotator_dev->imem_clk)
Ravishangar Kalyanamd16485d2012-03-21 16:51:26 -07001927 clk_prepare_enable(msm_rotator_dev->imem_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001928#endif
1929 enable_rot_clks();
1930 ver = ioread32(MSM_ROTATOR_HW_VERSION);
1931 disable_rot_clks();
1932
1933#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
1934 if (msm_rotator_dev->imem_clk)
Ravishangar Kalyanamd16485d2012-03-21 16:51:26 -07001935 clk_disable_unprepare(msm_rotator_dev->imem_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001936#endif
Naseer Ahmed18018602011-10-25 13:32:58 -07001937 if (ver != pdata->hardware_version_number)
Mayank Chopra012a8e72012-04-11 10:41:13 +05301938 pr_debug("%s: invalid HW version ver 0x%x\n",
Ravishangar Kalyanam6bc448a2012-03-14 11:31:52 -07001939 DRIVER_NAME, ver);
Naseer Ahmed18018602011-10-25 13:32:58 -07001940
Mayank Chopra012a8e72012-04-11 10:41:13 +05301941 rotator_hw_revision = ver;
1942 rotator_hw_revision >>= 16; /* bit 31:16 */
1943 rotator_hw_revision &= 0xff;
1944
1945 pr_info("%s: rotator_hw_revision=%x\n",
1946 __func__, rotator_hw_revision);
1947
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001948 msm_rotator_dev->irq = platform_get_irq(pdev, 0);
1949 if (msm_rotator_dev->irq < 0) {
1950 printk(KERN_ALERT "%s: could not get IORESOURCE_IRQ\n",
1951 DRIVER_NAME);
1952 rc = -ENODEV;
1953 goto error_get_irq;
1954 }
1955 rc = request_irq(msm_rotator_dev->irq, msm_rotator_isr,
1956 IRQF_TRIGGER_RISING, DRIVER_NAME, NULL);
1957 if (rc) {
1958 printk(KERN_ERR "%s: request_irq() failed\n", DRIVER_NAME);
1959 goto error_get_irq;
1960 }
1961 /* we enable the IRQ when we need it in the ioctl */
1962 disable_irq(msm_rotator_dev->irq);
1963
1964 rc = alloc_chrdev_region(&msm_rotator_dev->dev_num, 0, 1, DRIVER_NAME);
1965 if (rc < 0) {
1966 printk(KERN_ERR "%s: alloc_chrdev_region Failed rc = %d\n",
1967 __func__, rc);
1968 goto error_get_irq;
1969 }
1970
1971 msm_rotator_dev->class = class_create(THIS_MODULE, DRIVER_NAME);
1972 if (IS_ERR(msm_rotator_dev->class)) {
1973 rc = PTR_ERR(msm_rotator_dev->class);
1974 printk(KERN_ERR "%s: couldn't create class rc = %d\n",
1975 DRIVER_NAME, rc);
1976 goto error_class_create;
1977 }
1978
1979 msm_rotator_dev->device = device_create(msm_rotator_dev->class, NULL,
1980 msm_rotator_dev->dev_num, NULL,
1981 DRIVER_NAME);
1982 if (IS_ERR(msm_rotator_dev->device)) {
1983 rc = PTR_ERR(msm_rotator_dev->device);
1984 printk(KERN_ERR "%s: device_create failed %d\n",
1985 DRIVER_NAME, rc);
1986 goto error_class_device_create;
1987 }
1988
1989 cdev_init(&msm_rotator_dev->cdev, &msm_rotator_fops);
1990 rc = cdev_add(&msm_rotator_dev->cdev,
1991 MKDEV(MAJOR(msm_rotator_dev->dev_num), 0),
1992 1);
1993 if (rc < 0) {
1994 printk(KERN_ERR "%s: cdev_add failed %d\n", __func__, rc);
1995 goto error_cdev_add;
1996 }
1997
1998 init_waitqueue_head(&msm_rotator_dev->wq);
1999
2000 dev_dbg(msm_rotator_dev->device, "probe successful\n");
2001 return rc;
2002
2003error_cdev_add:
2004 device_destroy(msm_rotator_dev->class, msm_rotator_dev->dev_num);
2005error_class_device_create:
2006 class_destroy(msm_rotator_dev->class);
2007error_class_create:
2008 unregister_chrdev_region(msm_rotator_dev->dev_num, 1);
2009error_get_irq:
2010 iounmap(msm_rotator_dev->io_base);
2011error_get_resource:
2012 mutex_destroy(&msm_rotator_dev->rotator_lock);
2013 if (msm_rotator_dev->regulator)
2014 regulator_put(msm_rotator_dev->regulator);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002015 clk_put(msm_rotator_dev->core_clk);
2016error_core_clk:
2017 clk_put(msm_rotator_dev->pclk);
2018error_pclk:
2019 if (msm_rotator_dev->imem_clk)
2020 clk_put(msm_rotator_dev->imem_clk);
2021error_imem_clk:
2022 mutex_destroy(&msm_rotator_dev->imem_lock);
2023 kfree(msm_rotator_dev);
2024 return rc;
2025}
2026
2027static int __devexit msm_rotator_remove(struct platform_device *plat_dev)
2028{
2029 int i;
2030
Nagamalleswararao Ganjie1a9f872011-11-06 23:13:32 -08002031#ifdef CONFIG_MSM_BUS_SCALING
Huaibin Yang5bc13e02013-01-10 17:52:45 -08002032 if (msm_rotator_dev->bus_client_handle) {
2033 msm_bus_scale_unregister_client
2034 (msm_rotator_dev->bus_client_handle);
2035 msm_rotator_dev->bus_client_handle = 0;
2036 }
Nagamalleswararao Ganjie1a9f872011-11-06 23:13:32 -08002037#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002038 free_irq(msm_rotator_dev->irq, NULL);
2039 mutex_destroy(&msm_rotator_dev->rotator_lock);
2040 cdev_del(&msm_rotator_dev->cdev);
2041 device_destroy(msm_rotator_dev->class, msm_rotator_dev->dev_num);
2042 class_destroy(msm_rotator_dev->class);
2043 unregister_chrdev_region(msm_rotator_dev->dev_num, 1);
2044 iounmap(msm_rotator_dev->io_base);
2045 if (msm_rotator_dev->imem_clk) {
2046 if (msm_rotator_dev->imem_clk_state == CLK_EN)
Ravishangar Kalyanamd16485d2012-03-21 16:51:26 -07002047 clk_disable_unprepare(msm_rotator_dev->imem_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002048 clk_put(msm_rotator_dev->imem_clk);
2049 msm_rotator_dev->imem_clk = NULL;
2050 }
2051 if (msm_rotator_dev->rot_clk_state == CLK_EN)
2052 disable_rot_clks();
2053 clk_put(msm_rotator_dev->core_clk);
2054 clk_put(msm_rotator_dev->pclk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002055 if (msm_rotator_dev->regulator)
2056 regulator_put(msm_rotator_dev->regulator);
2057 msm_rotator_dev->core_clk = NULL;
2058 msm_rotator_dev->pclk = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002059 mutex_destroy(&msm_rotator_dev->imem_lock);
2060 for (i = 0; i < MAX_SESSIONS; i++)
Mayank Chopra27147a72012-07-17 13:57:22 +05302061 if (msm_rotator_dev->rot_session[i] != NULL)
2062 kfree(msm_rotator_dev->rot_session[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002063 kfree(msm_rotator_dev);
2064 return 0;
2065}
2066
2067#ifdef CONFIG_PM
2068static int msm_rotator_suspend(struct platform_device *dev, pm_message_t state)
2069{
2070 mutex_lock(&msm_rotator_dev->imem_lock);
2071 if (msm_rotator_dev->imem_clk_state == CLK_EN
2072 && msm_rotator_dev->imem_clk) {
Ravishangar Kalyanamd16485d2012-03-21 16:51:26 -07002073 clk_disable_unprepare(msm_rotator_dev->imem_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002074 msm_rotator_dev->imem_clk_state = CLK_SUSPEND;
2075 }
2076 mutex_unlock(&msm_rotator_dev->imem_lock);
2077 mutex_lock(&msm_rotator_dev->rotator_lock);
2078 if (msm_rotator_dev->rot_clk_state == CLK_EN) {
2079 disable_rot_clks();
2080 msm_rotator_dev->rot_clk_state = CLK_SUSPEND;
2081 }
2082 mutex_unlock(&msm_rotator_dev->rotator_lock);
2083 return 0;
2084}
2085
2086static int msm_rotator_resume(struct platform_device *dev)
2087{
2088 mutex_lock(&msm_rotator_dev->imem_lock);
2089 if (msm_rotator_dev->imem_clk_state == CLK_SUSPEND
2090 && msm_rotator_dev->imem_clk) {
Ravishangar Kalyanamd16485d2012-03-21 16:51:26 -07002091 clk_prepare_enable(msm_rotator_dev->imem_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002092 msm_rotator_dev->imem_clk_state = CLK_EN;
2093 }
2094 mutex_unlock(&msm_rotator_dev->imem_lock);
2095 mutex_lock(&msm_rotator_dev->rotator_lock);
2096 if (msm_rotator_dev->rot_clk_state == CLK_SUSPEND) {
2097 enable_rot_clks();
2098 msm_rotator_dev->rot_clk_state = CLK_EN;
2099 }
2100 mutex_unlock(&msm_rotator_dev->rotator_lock);
2101 return 0;
2102}
2103#endif
2104
2105static struct platform_driver msm_rotator_platform_driver = {
2106 .probe = msm_rotator_probe,
2107 .remove = __devexit_p(msm_rotator_remove),
2108#ifdef CONFIG_PM
2109 .suspend = msm_rotator_suspend,
2110 .resume = msm_rotator_resume,
2111#endif
2112 .driver = {
2113 .owner = THIS_MODULE,
2114 .name = DRIVER_NAME
2115 }
2116};
2117
2118static int __init msm_rotator_init(void)
2119{
2120 return platform_driver_register(&msm_rotator_platform_driver);
2121}
2122
2123static void __exit msm_rotator_exit(void)
2124{
2125 return platform_driver_unregister(&msm_rotator_platform_driver);
2126}
2127
2128module_init(msm_rotator_init);
2129module_exit(msm_rotator_exit);
2130
2131MODULE_DESCRIPTION("MSM Offline Image Rotator driver");
2132MODULE_VERSION("1.0");
2133MODULE_LICENSE("GPL v2");