blob: 58d98212ea4a74e2a0ef60e63f43357d9065002e [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
2 *
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>
22#include <mach/clk.h>
23#include <linux/android_pmem.h>
24#include <linux/msm_rotator.h>
25#include <linux/io.h>
26#include <mach/msm_rotator_imem.h>
27#include <linux/ktime.h>
28#include <linux/workqueue.h>
29#include <linux/file.h>
30#include <linux/major.h>
31#include <linux/regulator/consumer.h>
32
33#define DRIVER_NAME "msm_rotator"
34
35#define MSM_ROTATOR_BASE (msm_rotator_dev->io_base)
36#define MSM_ROTATOR_INTR_ENABLE (MSM_ROTATOR_BASE+0x0020)
37#define MSM_ROTATOR_INTR_STATUS (MSM_ROTATOR_BASE+0x0024)
38#define MSM_ROTATOR_INTR_CLEAR (MSM_ROTATOR_BASE+0x0028)
39#define MSM_ROTATOR_START (MSM_ROTATOR_BASE+0x0030)
40#define MSM_ROTATOR_MAX_BURST_SIZE (MSM_ROTATOR_BASE+0x0050)
41#define MSM_ROTATOR_HW_VERSION (MSM_ROTATOR_BASE+0x0070)
42#define MSM_ROTATOR_SRC_SIZE (MSM_ROTATOR_BASE+0x1108)
43#define MSM_ROTATOR_SRCP0_ADDR (MSM_ROTATOR_BASE+0x110c)
44#define MSM_ROTATOR_SRCP1_ADDR (MSM_ROTATOR_BASE+0x1110)
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -070045#define MSM_ROTATOR_SRCP2_ADDR (MSM_ROTATOR_BASE+0x1114)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070046#define MSM_ROTATOR_SRC_YSTRIDE1 (MSM_ROTATOR_BASE+0x111c)
47#define MSM_ROTATOR_SRC_YSTRIDE2 (MSM_ROTATOR_BASE+0x1120)
48#define MSM_ROTATOR_SRC_FORMAT (MSM_ROTATOR_BASE+0x1124)
49#define MSM_ROTATOR_SRC_UNPACK_PATTERN1 (MSM_ROTATOR_BASE+0x1128)
50#define MSM_ROTATOR_SUB_BLOCK_CFG (MSM_ROTATOR_BASE+0x1138)
51#define MSM_ROTATOR_OUT_PACK_PATTERN1 (MSM_ROTATOR_BASE+0x1154)
52#define MSM_ROTATOR_OUTP0_ADDR (MSM_ROTATOR_BASE+0x1168)
53#define MSM_ROTATOR_OUTP1_ADDR (MSM_ROTATOR_BASE+0x116c)
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -070054#define MSM_ROTATOR_OUTP2_ADDR (MSM_ROTATOR_BASE+0x1170)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070055#define MSM_ROTATOR_OUT_YSTRIDE1 (MSM_ROTATOR_BASE+0x1178)
56#define MSM_ROTATOR_OUT_YSTRIDE2 (MSM_ROTATOR_BASE+0x117c)
57#define MSM_ROTATOR_SRC_XY (MSM_ROTATOR_BASE+0x1200)
58#define MSM_ROTATOR_SRC_IMAGE_SIZE (MSM_ROTATOR_BASE+0x1208)
59
60#define MSM_ROTATOR_MAX_ROT 0x07
61#define MSM_ROTATOR_MAX_H 0x1fff
62#define MSM_ROTATOR_MAX_W 0x1fff
63
64/* from lsb to msb */
65#define GET_PACK_PATTERN(a, x, y, z, bit) \
66 (((a)<<((bit)*3))|((x)<<((bit)*2))|((y)<<(bit))|(z))
67#define CLR_G 0x0
68#define CLR_B 0x1
69#define CLR_R 0x2
70#define CLR_ALPHA 0x3
71
72#define CLR_Y CLR_G
73#define CLR_CB CLR_B
74#define CLR_CR CLR_R
75
76#define ROTATIONS_TO_BITMASK(r) ((((r) & MDP_ROT_90) ? 1 : 0) | \
77 (((r) & MDP_FLIP_LR) ? 2 : 0) | \
78 (((r) & MDP_FLIP_UD) ? 4 : 0))
79
80#define IMEM_NO_OWNER -1;
81
82#define MAX_SESSIONS 16
83#define INVALID_SESSION -1
84#define VERSION_KEY_MASK 0xFFFFFF00
85
86struct tile_parm {
87 unsigned int width; /* tile's width */
88 unsigned int height; /* tile's height */
89 unsigned int row_tile_w; /* tiles per row's width */
90 unsigned int row_tile_h; /* tiles per row's height */
91};
92
93struct msm_rotator_dev {
94 void __iomem *io_base;
95 int irq;
96 struct msm_rotator_img_info *img_info[MAX_SESSIONS];
97 struct clk *core_clk;
98 int pid_list[MAX_SESSIONS];
99 struct clk *pclk;
100 struct clk *axi_clk;
101 int rot_clk_state;
102 struct regulator *regulator;
103 struct delayed_work rot_clk_work;
104 struct clk *imem_clk;
105 int imem_clk_state;
106 struct delayed_work imem_clk_work;
107 struct platform_device *pdev;
108 struct cdev cdev;
109 struct device *device;
110 struct class *class;
111 dev_t dev_num;
112 int processing;
113 int last_session_idx;
114 struct mutex rotator_lock;
115 struct mutex imem_lock;
116 int imem_owner;
117 wait_queue_head_t wq;
118};
119
120#define chroma_addr(start, w, h, bpp) ((start) + ((h) * (w) * (bpp)))
121
122#define COMPONENT_5BITS 1
123#define COMPONENT_6BITS 2
124#define COMPONENT_8BITS 3
125
126static struct msm_rotator_dev *msm_rotator_dev;
127
128enum {
129 CLK_EN,
130 CLK_DIS,
131 CLK_SUSPEND,
132};
133
134int msm_rotator_imem_allocate(int requestor)
135{
136 int rc = 0;
137
138#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
139 switch (requestor) {
140 case ROTATOR_REQUEST:
141 if (mutex_trylock(&msm_rotator_dev->imem_lock)) {
142 msm_rotator_dev->imem_owner = ROTATOR_REQUEST;
143 rc = 1;
144 } else
145 rc = 0;
146 break;
147 case JPEG_REQUEST:
148 mutex_lock(&msm_rotator_dev->imem_lock);
149 msm_rotator_dev->imem_owner = JPEG_REQUEST;
150 rc = 1;
151 break;
152 default:
153 rc = 0;
154 }
155#else
156 if (requestor == JPEG_REQUEST)
157 rc = 1;
158#endif
159 if (rc == 1) {
160 cancel_delayed_work(&msm_rotator_dev->imem_clk_work);
161 if (msm_rotator_dev->imem_clk_state != CLK_EN
162 && msm_rotator_dev->imem_clk) {
163 clk_enable(msm_rotator_dev->imem_clk);
164 msm_rotator_dev->imem_clk_state = CLK_EN;
165 }
166 }
167
168 return rc;
169}
170EXPORT_SYMBOL(msm_rotator_imem_allocate);
171
172void msm_rotator_imem_free(int requestor)
173{
174#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
175 if (msm_rotator_dev->imem_owner == requestor) {
176 schedule_delayed_work(&msm_rotator_dev->imem_clk_work, HZ);
177 mutex_unlock(&msm_rotator_dev->imem_lock);
178 }
179#else
180 if (requestor == JPEG_REQUEST)
181 schedule_delayed_work(&msm_rotator_dev->imem_clk_work, HZ);
182#endif
183}
184EXPORT_SYMBOL(msm_rotator_imem_free);
185
186static void msm_rotator_imem_clk_work_f(struct work_struct *work)
187{
188#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
189 if (mutex_trylock(&msm_rotator_dev->imem_lock)) {
190 if (msm_rotator_dev->imem_clk_state == CLK_EN
191 && msm_rotator_dev->imem_clk) {
192 clk_disable(msm_rotator_dev->imem_clk);
193 msm_rotator_dev->imem_clk_state = CLK_DIS;
194 } else if (msm_rotator_dev->imem_clk_state == CLK_SUSPEND)
195 msm_rotator_dev->imem_clk_state = CLK_DIS;
196 mutex_unlock(&msm_rotator_dev->imem_lock);
197 }
198#endif
199}
200
201/* enable clocks needed by rotator block */
202static void enable_rot_clks(void)
203{
204 if (msm_rotator_dev->regulator)
205 regulator_enable(msm_rotator_dev->regulator);
206 if (msm_rotator_dev->core_clk != NULL)
207 clk_enable(msm_rotator_dev->core_clk);
208 if (msm_rotator_dev->pclk != NULL)
209 clk_enable(msm_rotator_dev->pclk);
210 if (msm_rotator_dev->axi_clk != NULL)
211 clk_enable(msm_rotator_dev->axi_clk);
212}
213
214/* disable clocks needed by rotator block */
215static void disable_rot_clks(void)
216{
217 if (msm_rotator_dev->core_clk != NULL)
218 clk_disable(msm_rotator_dev->core_clk);
219 if (msm_rotator_dev->pclk != NULL)
220 clk_disable(msm_rotator_dev->pclk);
221 if (msm_rotator_dev->axi_clk != NULL)
222 clk_disable(msm_rotator_dev->axi_clk);
223 if (msm_rotator_dev->regulator)
224 regulator_disable(msm_rotator_dev->regulator);
225}
226
227static void msm_rotator_rot_clk_work_f(struct work_struct *work)
228{
229 if (mutex_trylock(&msm_rotator_dev->rotator_lock)) {
230 if (msm_rotator_dev->rot_clk_state == CLK_EN) {
231 disable_rot_clks();
232 msm_rotator_dev->rot_clk_state = CLK_DIS;
233 } else if (msm_rotator_dev->rot_clk_state == CLK_SUSPEND)
234 msm_rotator_dev->rot_clk_state = CLK_DIS;
235 mutex_unlock(&msm_rotator_dev->rotator_lock);
236 }
237}
238
239static irqreturn_t msm_rotator_isr(int irq, void *dev_id)
240{
241 if (msm_rotator_dev->processing) {
242 msm_rotator_dev->processing = 0;
243 wake_up(&msm_rotator_dev->wq);
244 } else
245 printk(KERN_WARNING "%s: unexpected interrupt\n", DRIVER_NAME);
246
247 return IRQ_HANDLED;
248}
249
250static int get_bpp(int format)
251{
252 switch (format) {
253 case MDP_RGB_565:
254 case MDP_BGR_565:
255 return 2;
256
257 case MDP_XRGB_8888:
258 case MDP_ARGB_8888:
259 case MDP_RGBA_8888:
260 case MDP_BGRA_8888:
261 case MDP_RGBX_8888:
262 return 4;
263
264 case MDP_Y_CBCR_H2V2:
265 case MDP_Y_CRCB_H2V2:
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -0700266 case MDP_Y_CB_CR_H2V2:
267 case MDP_Y_CR_CB_H2V2:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700268 case MDP_Y_CRCB_H2V2_TILE:
269 case MDP_Y_CBCR_H2V2_TILE:
270 return 1;
271
272 case MDP_RGB_888:
273 return 3;
274
275 case MDP_YCRYCB_H2V1:
276 return 2;/* YCrYCb interleave */
277
278 case MDP_Y_CRCB_H2V1:
279 case MDP_Y_CBCR_H2V1:
280 return 1;
281
282 default:
283 return -1;
284 }
285
286}
287
288static int msm_rotator_ycxcx_h2v1(struct msm_rotator_img_info *info,
289 unsigned int in_paddr,
290 unsigned int out_paddr,
291 unsigned int use_imem,
292 int new_session,
293 unsigned int in_chroma_paddr,
294 unsigned int out_chroma_paddr)
295{
296 int bpp;
297 unsigned int in_chr_addr, out_chr_addr;
298
299 if (info->src.format != info->dst.format)
300 return -EINVAL;
301
302 bpp = get_bpp(info->src.format);
303 if (bpp < 0)
304 return -ENOTTY;
305
306 if (!in_chroma_paddr) {
307 in_chr_addr = chroma_addr(in_paddr, info->src.width,
308 info->src.height,
309 bpp);
310 } else
311 in_chr_addr = in_chroma_paddr;
312
313 if (!out_chroma_paddr) {
314 out_chr_addr = chroma_addr(out_paddr, info->dst.width,
315 info->dst.height,
316 bpp);
317 } else
318 out_chr_addr = out_chroma_paddr;
319
320 iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
321
322 iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
323 iowrite32(in_chr_addr, MSM_ROTATOR_SRCP1_ADDR);
324 iowrite32(out_paddr +
325 ((info->dst_y * info->dst.width) + info->dst_x),
326 MSM_ROTATOR_OUTP0_ADDR);
327 iowrite32(out_chr_addr +
328 ((info->dst_y * info->dst.width) + info->dst_x),
329 MSM_ROTATOR_OUTP1_ADDR);
330
331 if (new_session) {
332 iowrite32(info->src.width |
333 info->src.width << 16,
334 MSM_ROTATOR_SRC_YSTRIDE1);
335 if (info->rotations & MDP_ROT_90)
336 iowrite32(info->dst.width |
337 info->dst.width*2 << 16,
338 MSM_ROTATOR_OUT_YSTRIDE1);
339 else
340 iowrite32(info->dst.width |
341 info->dst.width << 16,
342 MSM_ROTATOR_OUT_YSTRIDE1);
343 if (info->src.format == MDP_Y_CBCR_H2V1) {
344 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
345 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
346 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
347 MSM_ROTATOR_OUT_PACK_PATTERN1);
348 } else {
349 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
350 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
351 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
352 MSM_ROTATOR_OUT_PACK_PATTERN1);
353 }
354 iowrite32((1 << 18) | /* chroma sampling 1=H2V1 */
355 (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
356 1 << 8, /* ROT_EN */
357 MSM_ROTATOR_SUB_BLOCK_CFG);
358 iowrite32(0 << 29 | /* frame format 0 = linear */
359 (use_imem ? 0 : 1) << 22 | /* tile size */
360 2 << 19 | /* fetch planes 2 = pseudo */
361 0 << 18 | /* unpack align */
362 1 << 17 | /* unpack tight */
363 1 << 13 | /* unpack count 0=1 component */
364 (bpp-1) << 9 | /* src Bpp 0=1 byte ... */
365 0 << 8 | /* has alpha */
366 0 << 6 | /* alpha bits 3=8bits */
367 3 << 4 | /* R/Cr bits 1=5 2=6 3=8 */
368 3 << 2 | /* B/Cb bits 1=5 2=6 3=8 */
369 3 << 0, /* G/Y bits 1=5 2=6 3=8 */
370 MSM_ROTATOR_SRC_FORMAT);
371 }
372
373 return 0;
374}
375
376static int msm_rotator_ycxcx_h2v2(struct msm_rotator_img_info *info,
377 unsigned int in_paddr,
378 unsigned int out_paddr,
379 unsigned int use_imem,
380 int new_session,
381 unsigned int in_chroma_paddr,
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -0700382 unsigned int out_chroma_paddr,
383 int is_planar)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700384{
385 int bpp;
386 unsigned int in_chr_addr, out_chr_addr;
387
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700388 bpp = get_bpp(info->src.format);
389 if (bpp < 0)
390 return -ENOTTY;
391
392 if (!in_chroma_paddr) {
393 in_chr_addr = chroma_addr(in_paddr, info->src.width,
394 info->src.height,
395 bpp);
396 } else
397 in_chr_addr = in_chroma_paddr;
398
399 if (!out_chroma_paddr) {
400 out_chr_addr = chroma_addr(out_paddr, info->dst.width,
401 info->dst.height,
402 bpp);
403 } else
404 out_chr_addr = out_chroma_paddr;
405
406 iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
407 iowrite32(in_chr_addr,
408 MSM_ROTATOR_SRCP1_ADDR);
409 iowrite32(out_paddr +
410 ((info->dst_y * info->dst.width) + info->dst_x),
411 MSM_ROTATOR_OUTP0_ADDR);
412 iowrite32(out_chr_addr +
413 ((info->dst_y * info->dst.width)/2 + info->dst_x),
414 MSM_ROTATOR_OUTP1_ADDR);
415
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -0700416 if (is_planar) {
417 iowrite32(in_chr_addr +
418 (info->src.width / 2) * (info->src.height / 2),
419 MSM_ROTATOR_SRCP2_ADDR);
420 }
421
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700422 if (new_session) {
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -0700423 if (is_planar) {
424 iowrite32(info->src.width |
425 (info->src.width / 2) << 16,
426 MSM_ROTATOR_SRC_YSTRIDE1);
427 iowrite32((info->src.width / 2),
428 MSM_ROTATOR_SRC_YSTRIDE2);
429 } else {
430 iowrite32(info->src.width |
431 info->src.width << 16,
432 MSM_ROTATOR_SRC_YSTRIDE1);
433 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700434 iowrite32(info->dst.width |
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -0700435 info->dst.width << 16,
436 MSM_ROTATOR_OUT_YSTRIDE1);
437
438 if ((info->src.format == MDP_Y_CBCR_H2V2) ||
439 (info->src.format == MDP_Y_CB_CR_H2V2)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700440 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
441 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
442 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
443 MSM_ROTATOR_OUT_PACK_PATTERN1);
444 } else {
445 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
446 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
447 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
448 MSM_ROTATOR_OUT_PACK_PATTERN1);
449 }
450 iowrite32((3 << 18) | /* chroma sampling 3=4:2:0 */
451 (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
452 1 << 8, /* ROT_EN */
453 MSM_ROTATOR_SUB_BLOCK_CFG);
454 iowrite32(0 << 29 | /* frame format 0 = linear */
455 (use_imem ? 0 : 1) << 22 | /* tile size */
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -0700456 (is_planar ? 1 : 2) << 19 | /* fetch planes */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700457 0 << 18 | /* unpack align */
458 1 << 17 | /* unpack tight */
459 1 << 13 | /* unpack count 0=1 component */
460 (bpp-1) << 9 | /* src Bpp 0=1 byte ... */
461 0 << 8 | /* has alpha */
462 0 << 6 | /* alpha bits 3=8bits */
463 3 << 4 | /* R/Cr bits 1=5 2=6 3=8 */
464 3 << 2 | /* B/Cb bits 1=5 2=6 3=8 */
465 3 << 0, /* G/Y bits 1=5 2=6 3=8 */
466 MSM_ROTATOR_SRC_FORMAT);
467 }
468 return 0;
469}
470
471static unsigned int tile_size(unsigned int src_width,
472 unsigned int src_height,
473 const struct tile_parm *tp)
474{
475 unsigned int tile_w, tile_h;
476 unsigned int row_num_w, row_num_h;
477 tile_w = tp->width * tp->row_tile_w;
478 tile_h = tp->height * tp->row_tile_h;
479 row_num_w = (src_width + tile_w - 1) / tile_w;
480 row_num_h = (src_height + tile_h - 1) / tile_h;
481 return ((row_num_w * row_num_h * tile_w * tile_h) + 8191) & ~8191;
482}
483
484static int msm_rotator_ycxcx_h2v2_tile(struct msm_rotator_img_info *info,
485 unsigned int in_paddr,
486 unsigned int out_paddr,
487 unsigned int use_imem,
488 int new_session,
489 unsigned in_chroma_paddr,
490 unsigned out_chroma_paddr)
491{
492 int bpp;
493 unsigned int offset = 0;
494 unsigned int in_chr_addr, out_chr_addr;
495 /*
496 * each row of samsung tile consists of two tiles in height
497 * and two tiles in width which means width should align to
498 * 64 x 2 bytes and height should align to 32 x 2 bytes.
499 * video decoder generate two tiles in width and one tile
500 * in height which ends up height align to 32 X 1 bytes.
501 */
502 const struct tile_parm tile = {64, 32, 2, 1};
503 if ((info->src.format == MDP_Y_CRCB_H2V2_TILE &&
504 info->dst.format != MDP_Y_CRCB_H2V2) ||
505 (info->src.format == MDP_Y_CBCR_H2V2_TILE &&
506 info->dst.format != MDP_Y_CBCR_H2V2))
507 return -EINVAL;
508
509 bpp = get_bpp(info->src.format);
510 if (bpp < 0)
511 return -ENOTTY;
512
513 offset = tile_size(info->src.width, info->src.height, &tile);
514 if (!in_chroma_paddr)
515 in_chr_addr = in_paddr + offset;
516 else
517 in_chr_addr = in_chroma_paddr;
518
519 if (!out_chroma_paddr) {
520 out_chr_addr = chroma_addr(out_paddr, info->dst.width,
521 info->dst.height,
522 bpp);
523 } else
524 out_chr_addr = out_chroma_paddr;
525
526 iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
527 iowrite32(in_paddr + offset, MSM_ROTATOR_SRCP1_ADDR);
528 iowrite32(out_paddr +
529 ((info->dst_y * info->dst.width) + info->dst_x),
530 MSM_ROTATOR_OUTP0_ADDR);
531 iowrite32(out_chr_addr +
532 ((info->dst_y * info->dst.width)/2 + info->dst_x),
533 MSM_ROTATOR_OUTP1_ADDR);
534
535 if (new_session) {
536 iowrite32(info->src.width |
537 info->src.width << 16,
538 MSM_ROTATOR_SRC_YSTRIDE1);
539
540 iowrite32(info->dst.width |
541 info->dst.width << 16,
542 MSM_ROTATOR_OUT_YSTRIDE1);
543 if (info->src.format == MDP_Y_CBCR_H2V2_TILE) {
544 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
545 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
546 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
547 MSM_ROTATOR_OUT_PACK_PATTERN1);
548 } else {
549 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
550 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
551 iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
552 MSM_ROTATOR_OUT_PACK_PATTERN1);
553 }
554 iowrite32((3 << 18) | /* chroma sampling 3=4:2:0 */
555 (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
556 1 << 8, /* ROT_EN */
557 MSM_ROTATOR_SUB_BLOCK_CFG);
558 iowrite32(2 << 29 | /* frame format 2 = supertile */
559 (use_imem ? 0 : 1) << 22 | /* tile size */
560 2 << 19 | /* fetch planes 2 = pseudo */
561 0 << 18 | /* unpack align */
562 1 << 17 | /* unpack tight */
563 1 << 13 | /* unpack count 0=1 component */
564 (bpp-1) << 9 | /* src Bpp 0=1 byte ... */
565 0 << 8 | /* has alpha */
566 0 << 6 | /* alpha bits 3=8bits */
567 3 << 4 | /* R/Cr bits 1=5 2=6 3=8 */
568 3 << 2 | /* B/Cb bits 1=5 2=6 3=8 */
569 3 << 0, /* G/Y bits 1=5 2=6 3=8 */
570 MSM_ROTATOR_SRC_FORMAT);
571 }
572 return 0;
573}
574
575static int msm_rotator_ycrycb(struct msm_rotator_img_info *info,
576 unsigned int in_paddr,
577 unsigned int out_paddr,
578 unsigned int use_imem,
579 int new_session)
580{
581 int bpp;
582
583 if (info->src.format != info->dst.format)
584 return -EINVAL;
585
586 bpp = get_bpp(info->src.format);
587 if (bpp < 0)
588 return -ENOTTY;
589
590 iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
591 iowrite32(out_paddr +
592 ((info->dst_y * info->dst.width) + info->dst_x),
593 MSM_ROTATOR_OUTP0_ADDR);
594
595 if (new_session) {
596 iowrite32(info->src.width,
597 MSM_ROTATOR_SRC_YSTRIDE1);
598 iowrite32(info->dst.width,
599 MSM_ROTATOR_OUT_YSTRIDE1);
600 iowrite32(GET_PACK_PATTERN(CLR_Y, CLR_CR, CLR_Y, CLR_CB, 8),
601 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
602 iowrite32(GET_PACK_PATTERN(CLR_Y, CLR_CR, CLR_Y, CLR_CB, 8),
603 MSM_ROTATOR_OUT_PACK_PATTERN1);
604 iowrite32((1 << 18) | /* chroma sampling 1=H2V1 */
605 (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
606 1 << 8, /* ROT_EN */
607 MSM_ROTATOR_SUB_BLOCK_CFG);
608 iowrite32(0 << 29 | /* frame format 0 = linear */
609 (use_imem ? 0 : 1) << 22 | /* tile size */
610 0 << 19 | /* fetch planes 0=interleaved */
611 0 << 18 | /* unpack align */
612 1 << 17 | /* unpack tight */
613 3 << 13 | /* unpack count 0=1 component */
614 (bpp-1) << 9 | /* src Bpp 0=1 byte ... */
615 0 << 8 | /* has alpha */
616 0 << 6 | /* alpha bits 3=8bits */
617 3 << 4 | /* R/Cr bits 1=5 2=6 3=8 */
618 3 << 2 | /* B/Cb bits 1=5 2=6 3=8 */
619 3 << 0, /* G/Y bits 1=5 2=6 3=8 */
620 MSM_ROTATOR_SRC_FORMAT);
621 }
622
623 return 0;
624}
625
626static int msm_rotator_rgb_types(struct msm_rotator_img_info *info,
627 unsigned int in_paddr,
628 unsigned int out_paddr,
629 unsigned int use_imem,
630 int new_session)
631{
632 int bpp, abits, rbits, gbits, bbits;
633
634 if (info->src.format != info->dst.format)
635 return -EINVAL;
636
637 bpp = get_bpp(info->src.format);
638 if (bpp < 0)
639 return -ENOTTY;
640
641 iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
642 iowrite32(out_paddr +
643 ((info->dst_y * info->dst.width) + info->dst_x) * bpp,
644 MSM_ROTATOR_OUTP0_ADDR);
645
646 if (new_session) {
647 iowrite32(info->src.width * bpp, MSM_ROTATOR_SRC_YSTRIDE1);
648 iowrite32(info->dst.width * bpp, MSM_ROTATOR_OUT_YSTRIDE1);
649 iowrite32((0 << 18) | /* chroma sampling 0=rgb */
650 (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
651 1 << 8, /* ROT_EN */
652 MSM_ROTATOR_SUB_BLOCK_CFG);
653 switch (info->src.format) {
654 case MDP_RGB_565:
655 iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
656 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
657 iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
658 MSM_ROTATOR_OUT_PACK_PATTERN1);
659 abits = 0;
660 rbits = COMPONENT_5BITS;
661 gbits = COMPONENT_6BITS;
662 bbits = COMPONENT_5BITS;
663 break;
664
665 case MDP_BGR_565:
666 iowrite32(GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
667 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
668 iowrite32(GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
669 MSM_ROTATOR_OUT_PACK_PATTERN1);
670 abits = 0;
671 rbits = COMPONENT_5BITS;
672 gbits = COMPONENT_6BITS;
673 bbits = COMPONENT_5BITS;
674 break;
675
676 case MDP_RGB_888:
677 iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
678 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
679 iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
680 MSM_ROTATOR_OUT_PACK_PATTERN1);
681 abits = 0;
682 rbits = COMPONENT_8BITS;
683 gbits = COMPONENT_8BITS;
684 bbits = COMPONENT_8BITS;
685 break;
686
687 case MDP_ARGB_8888:
688 case MDP_RGBA_8888:
689 case MDP_XRGB_8888:
690 case MDP_RGBX_8888:
691 iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G,
692 CLR_B, 8),
693 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
694 iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G,
695 CLR_B, 8),
696 MSM_ROTATOR_OUT_PACK_PATTERN1);
697 abits = COMPONENT_8BITS;
698 rbits = COMPONENT_8BITS;
699 gbits = COMPONENT_8BITS;
700 bbits = COMPONENT_8BITS;
701 break;
702
703 case MDP_BGRA_8888:
704 iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G,
705 CLR_R, 8),
706 MSM_ROTATOR_SRC_UNPACK_PATTERN1);
707 iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G,
708 CLR_R, 8),
709 MSM_ROTATOR_OUT_PACK_PATTERN1);
710 abits = COMPONENT_8BITS;
711 rbits = COMPONENT_8BITS;
712 gbits = COMPONENT_8BITS;
713 bbits = COMPONENT_8BITS;
714 break;
715
716 default:
717 return -EINVAL;
718 }
719 iowrite32(0 << 29 | /* frame format 0 = linear */
720 (use_imem ? 0 : 1) << 22 | /* tile size */
721 0 << 19 | /* fetch planes 0=interleaved */
722 0 << 18 | /* unpack align */
723 1 << 17 | /* unpack tight */
724 (abits ? 3 : 2) << 13 | /* unpack count 0=1 comp */
725 (bpp-1) << 9 | /* src Bpp 0=1 byte ... */
726 (abits ? 1 : 0) << 8 | /* has alpha */
727 abits << 6 | /* alpha bits 3=8bits */
728 rbits << 4 | /* R/Cr bits 1=5 2=6 3=8 */
729 bbits << 2 | /* B/Cb bits 1=5 2=6 3=8 */
730 gbits << 0, /* G/Y bits 1=5 2=6 3=8 */
731 MSM_ROTATOR_SRC_FORMAT);
732 }
733
734 return 0;
735}
736
737static int get_img(int memory_id, unsigned long *start, unsigned long *len,
738 struct file **pp_file)
739{
740 int ret = 0;
741#ifdef CONFIG_FB
742 struct file *file;
743 int put_needed, fb_num;
744#endif
745#ifdef CONFIG_ANDROID_PMEM
746 unsigned long vstart;
747#endif
748
749#ifdef CONFIG_ANDROID_PMEM
750 if (!get_pmem_file(memory_id, start, &vstart, len, pp_file))
751 return 0;
752#endif
753#ifdef CONFIG_FB
754 file = fget_light(memory_id, &put_needed);
755 if (file == NULL)
756 return -1;
757
758 if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) {
759 fb_num = MINOR(file->f_dentry->d_inode->i_rdev);
760 if (get_fb_phys_info(start, len, fb_num))
761 ret = -1;
762 else
763 *pp_file = file;
764 } else
765 ret = -1;
766 if (ret)
767 fput_light(file, put_needed);
768#endif
769 return ret;
770}
771
772static int msm_rotator_do_rotate(unsigned long arg)
773{
774 int rc = 0;
775 unsigned int status;
776 struct msm_rotator_data_info info;
777 unsigned int in_paddr, out_paddr;
778 unsigned long len;
779 struct file *src_file = 0;
780 struct file *dst_file = 0;
781 int use_imem = 0;
782 int s;
783 struct file *src_chroma_file = 0;
784 struct file *dst_chroma_file = 0;
785 unsigned int in_chroma_paddr = 0, out_chroma_paddr = 0;
786 uint32_t format;
787
788 if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
789 return -EFAULT;
790
791 rc = get_img(info.src.memory_id, (unsigned long *)&in_paddr,
792 (unsigned long *)&len, &src_file);
793 if (rc) {
794 printk(KERN_ERR "%s: in get_img() failed id=0x%08x\n",
795 DRIVER_NAME, info.src.memory_id);
796 return rc;
797 }
798 in_paddr += info.src.offset;
799
800 rc = get_img(info.dst.memory_id, (unsigned long *)&out_paddr,
801 (unsigned long *)&len, &dst_file);
802 if (rc) {
803 printk(KERN_ERR "%s: out get_img() failed id=0x%08x\n",
804 DRIVER_NAME, info.dst.memory_id);
805 goto do_rotate_fail_dst_img;
806 }
807 out_paddr += info.dst.offset;
808
809 mutex_lock(&msm_rotator_dev->rotator_lock);
810 for (s = 0; s < MAX_SESSIONS; s++)
811 if ((msm_rotator_dev->img_info[s] != NULL) &&
812 (info.session_id ==
813 (unsigned int)msm_rotator_dev->img_info[s]
814 ))
815 break;
816
817 if (s == MAX_SESSIONS) {
818 dev_dbg(msm_rotator_dev->device,
819 "%s() : Attempt to use invalid session_id %d\n",
820 __func__, s);
821 rc = -EINVAL;
822 goto do_rotate_unlock_mutex;
823 }
824
825 if (msm_rotator_dev->img_info[s]->enable == 0) {
826 dev_dbg(msm_rotator_dev->device,
827 "%s() : Session_id %d not enabled \n",
828 __func__, s);
829 rc = -EINVAL;
830 goto do_rotate_unlock_mutex;
831 }
832
833 format = msm_rotator_dev->img_info[s]->src.format;
834 if (((info.version_key & VERSION_KEY_MASK) == 0xA5B4C300) &&
835 ((info.version_key & ~VERSION_KEY_MASK) > 0) &&
836 (format == MDP_Y_CBCR_H2V2 ||
837 format == MDP_Y_CRCB_H2V2 ||
838 format == MDP_Y_CRCB_H2V2_TILE ||
839 format == MDP_Y_CBCR_H2V2_TILE ||
840 format == MDP_Y_CBCR_H2V1 ||
841 format == MDP_Y_CRCB_H2V1)) {
842 rc = get_img(info.src_chroma.memory_id,
843 (unsigned long *)&in_chroma_paddr,
844 (unsigned long *)&len, &src_chroma_file);
845 if (rc) {
846 printk(KERN_ERR "%s: in chroma get_img() failed id=0x%08x\n",
847 DRIVER_NAME, info.src_chroma.memory_id);
848 goto do_rotate_unlock_mutex;
849 }
850 in_chroma_paddr += info.src_chroma.offset;
851
852 rc = get_img(info.dst_chroma.memory_id,
853 (unsigned long *)&out_chroma_paddr,
854 (unsigned long *)&len, &dst_chroma_file);
855 if (rc) {
856 printk(KERN_ERR "%s: out chroma get_img() failed id=0x%08x\n",
857 DRIVER_NAME, info.dst_chroma.memory_id);
858 goto do_rotate_fail_dst_chr_img;
859 }
860 out_chroma_paddr += info.dst_chroma.offset;
861 }
862
863 cancel_delayed_work(&msm_rotator_dev->rot_clk_work);
864 if (msm_rotator_dev->rot_clk_state != CLK_EN) {
865 enable_rot_clks();
866 msm_rotator_dev->rot_clk_state = CLK_EN;
867 }
868 enable_irq(msm_rotator_dev->irq);
869
870#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
871 use_imem = msm_rotator_imem_allocate(ROTATOR_REQUEST);
872#else
873 use_imem = 0;
874#endif
875 /*
876 * workaround for a hardware bug. rotator hardware hangs when we
877 * use write burst beat size 16 on 128X128 tile fetch mode. As a
878 * temporary fix use 0x42 for BURST_SIZE when imem used.
879 */
880 if (use_imem)
881 iowrite32(0x42, MSM_ROTATOR_MAX_BURST_SIZE);
882
883 iowrite32(((msm_rotator_dev->img_info[s]->src_rect.h & 0x1fff)
884 << 16) |
885 (msm_rotator_dev->img_info[s]->src_rect.w & 0x1fff),
886 MSM_ROTATOR_SRC_SIZE);
887 iowrite32(((msm_rotator_dev->img_info[s]->src_rect.y & 0x1fff)
888 << 16) |
889 (msm_rotator_dev->img_info[s]->src_rect.x & 0x1fff),
890 MSM_ROTATOR_SRC_XY);
891 iowrite32(((msm_rotator_dev->img_info[s]->src.height & 0x1fff)
892 << 16) |
893 (msm_rotator_dev->img_info[s]->src.width & 0x1fff),
894 MSM_ROTATOR_SRC_IMAGE_SIZE);
895
896 switch (format) {
897 case MDP_RGB_565:
898 case MDP_BGR_565:
899 case MDP_RGB_888:
900 case MDP_ARGB_8888:
901 case MDP_RGBA_8888:
902 case MDP_XRGB_8888:
903 case MDP_BGRA_8888:
904 case MDP_RGBX_8888:
905 rc = msm_rotator_rgb_types(msm_rotator_dev->img_info[s],
906 in_paddr, out_paddr,
907 use_imem,
908 msm_rotator_dev->last_session_idx
909 != s);
910 break;
911 case MDP_Y_CBCR_H2V2:
912 case MDP_Y_CRCB_H2V2:
913 rc = msm_rotator_ycxcx_h2v2(msm_rotator_dev->img_info[s],
914 in_paddr, out_paddr, use_imem,
915 msm_rotator_dev->last_session_idx
916 != s,
917 in_chroma_paddr,
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -0700918 out_chroma_paddr,
919 0);
920 break;
921 case MDP_Y_CB_CR_H2V2:
922 case MDP_Y_CR_CB_H2V2:
923 rc = msm_rotator_ycxcx_h2v2(msm_rotator_dev->img_info[s],
924 in_paddr, out_paddr, use_imem,
925 msm_rotator_dev->last_session_idx
926 != s,
927 in_chroma_paddr,
928 out_chroma_paddr,
929 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700930 break;
931 case MDP_Y_CRCB_H2V2_TILE:
932 case MDP_Y_CBCR_H2V2_TILE:
933 rc = msm_rotator_ycxcx_h2v2_tile(msm_rotator_dev->img_info[s],
934 in_paddr, out_paddr, use_imem,
935 msm_rotator_dev->last_session_idx
936 != s,
937 in_chroma_paddr,
938 out_chroma_paddr);
939 break;
940
941 case MDP_Y_CBCR_H2V1:
942 case MDP_Y_CRCB_H2V1:
943 rc = msm_rotator_ycxcx_h2v1(msm_rotator_dev->img_info[s],
944 in_paddr, out_paddr, use_imem,
945 msm_rotator_dev->last_session_idx
946 != s,
947 in_chroma_paddr,
948 out_chroma_paddr);
949 break;
950 case MDP_YCRYCB_H2V1:
951 rc = msm_rotator_ycrycb(msm_rotator_dev->img_info[s],
952 in_paddr, out_paddr, use_imem,
953 msm_rotator_dev->last_session_idx != s);
954 break;
955 default:
956 rc = -EINVAL;
957 goto do_rotate_exit;
958 }
959
960 if (rc != 0) {
961 msm_rotator_dev->last_session_idx = INVALID_SESSION;
962 goto do_rotate_exit;
963 }
964
965 iowrite32(3, MSM_ROTATOR_INTR_ENABLE);
966
967 msm_rotator_dev->processing = 1;
968 iowrite32(0x1, MSM_ROTATOR_START);
969
970 wait_event(msm_rotator_dev->wq,
971 (msm_rotator_dev->processing == 0));
972 status = (unsigned char)ioread32(MSM_ROTATOR_INTR_STATUS);
973 if ((status & 0x03) != 0x01)
974 rc = -EFAULT;
975 iowrite32(0, MSM_ROTATOR_INTR_ENABLE);
976 iowrite32(3, MSM_ROTATOR_INTR_CLEAR);
977
978do_rotate_exit:
979 disable_irq(msm_rotator_dev->irq);
980#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
981 msm_rotator_imem_free(ROTATOR_REQUEST);
982#endif
983 schedule_delayed_work(&msm_rotator_dev->rot_clk_work, HZ);
984 if (dst_chroma_file)
985 put_pmem_file(dst_chroma_file);
986do_rotate_fail_dst_chr_img:
987 if (src_chroma_file)
988 put_pmem_file(src_chroma_file);
989do_rotate_unlock_mutex:
990 mutex_unlock(&msm_rotator_dev->rotator_lock);
991 if (dst_file)
992 put_pmem_file(dst_file);
993do_rotate_fail_dst_img:
994 if (src_file)
995 put_pmem_file(src_file);
996 dev_dbg(msm_rotator_dev->device, "%s() returning rc = %d\n",
997 __func__, rc);
998 return rc;
999}
1000
1001static int msm_rotator_start(unsigned long arg, int pid)
1002{
1003 struct msm_rotator_img_info info;
1004 int rc = 0;
1005 int s;
1006 int first_free_index = INVALID_SESSION;
1007
1008 if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
1009 return -EFAULT;
1010
1011 if ((info.rotations > MSM_ROTATOR_MAX_ROT) ||
1012 (info.src.height > MSM_ROTATOR_MAX_H) ||
1013 (info.src.width > MSM_ROTATOR_MAX_W) ||
1014 (info.dst.height > MSM_ROTATOR_MAX_H) ||
1015 (info.dst.width > MSM_ROTATOR_MAX_W) ||
1016 ((info.src_rect.x + info.src_rect.w) > info.src.width) ||
1017 ((info.src_rect.y + info.src_rect.h) > info.src.height) ||
1018 ((info.rotations & MDP_ROT_90) &&
1019 ((info.dst_x + info.src_rect.h) > info.dst.width)) ||
1020 ((info.rotations & MDP_ROT_90) &&
1021 ((info.dst_y + info.src_rect.w) > info.dst.height)) ||
1022 (!(info.rotations & MDP_ROT_90) &&
1023 ((info.dst_x + info.src_rect.w) > info.dst.width)) ||
1024 (!(info.rotations & MDP_ROT_90) &&
1025 ((info.dst_y + info.src_rect.h) > info.dst.height)))
1026 return -EINVAL;
1027
1028 switch (info.src.format) {
1029 case MDP_RGB_565:
1030 case MDP_BGR_565:
1031 case MDP_RGB_888:
1032 case MDP_ARGB_8888:
1033 case MDP_RGBA_8888:
1034 case MDP_XRGB_8888:
1035 case MDP_RGBX_8888:
1036 case MDP_BGRA_8888:
1037 case MDP_Y_CBCR_H2V2:
1038 case MDP_Y_CRCB_H2V2:
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -07001039 case MDP_Y_CB_CR_H2V2:
1040 case MDP_Y_CR_CB_H2V2:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001041 case MDP_Y_CBCR_H2V1:
1042 case MDP_Y_CRCB_H2V1:
1043 case MDP_YCRYCB_H2V1:
1044 case MDP_Y_CRCB_H2V2_TILE:
1045 case MDP_Y_CBCR_H2V2_TILE:
1046 break;
1047 default:
1048 return -EINVAL;
1049 }
1050
1051 switch (info.dst.format) {
1052 case MDP_RGB_565:
1053 case MDP_BGR_565:
1054 case MDP_RGB_888:
1055 case MDP_ARGB_8888:
1056 case MDP_RGBA_8888:
1057 case MDP_XRGB_8888:
1058 case MDP_RGBX_8888:
1059 case MDP_BGRA_8888:
1060 case MDP_Y_CBCR_H2V2:
1061 case MDP_Y_CRCB_H2V2:
Adrian Salido-Moreno0644f342011-07-08 12:05:01 -07001062 case MDP_Y_CB_CR_H2V2:
1063 case MDP_Y_CR_CB_H2V2:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001064 case MDP_Y_CBCR_H2V1:
1065 case MDP_Y_CRCB_H2V1:
1066 case MDP_YCRYCB_H2V1:
1067 break;
1068 default:
1069 return -EINVAL;
1070 }
1071
1072 mutex_lock(&msm_rotator_dev->rotator_lock);
1073 for (s = 0; s < MAX_SESSIONS; s++) {
1074 if ((msm_rotator_dev->img_info[s] != NULL) &&
1075 (info.session_id ==
1076 (unsigned int)msm_rotator_dev->img_info[s]
1077 )) {
1078 *(msm_rotator_dev->img_info[s]) = info;
1079 msm_rotator_dev->pid_list[s] = pid;
1080
1081 if (msm_rotator_dev->last_session_idx == s)
1082 msm_rotator_dev->last_session_idx =
1083 INVALID_SESSION;
1084 break;
1085 }
1086
1087 if ((msm_rotator_dev->img_info[s] == NULL) &&
1088 (first_free_index ==
1089 INVALID_SESSION))
1090 first_free_index = s;
1091 }
1092
1093 if ((s == MAX_SESSIONS) && (first_free_index != INVALID_SESSION)) {
1094 /* allocate a session id */
1095 msm_rotator_dev->img_info[first_free_index] =
1096 kzalloc(sizeof(struct msm_rotator_img_info),
1097 GFP_KERNEL);
1098 if (!msm_rotator_dev->img_info[first_free_index]) {
1099 printk(KERN_ERR "%s : unable to alloc mem\n",
1100 __func__);
1101 rc = -ENOMEM;
1102 goto rotator_start_exit;
1103 }
1104 info.session_id = (unsigned int)
1105 msm_rotator_dev->img_info[first_free_index];
1106 *(msm_rotator_dev->img_info[first_free_index]) = info;
1107 msm_rotator_dev->pid_list[first_free_index] = pid;
1108
1109 if (copy_to_user((void __user *)arg, &info, sizeof(info)))
1110 rc = -EFAULT;
1111 } else if (s == MAX_SESSIONS) {
1112 dev_dbg(msm_rotator_dev->device, "%s: all sessions in use\n",
1113 __func__);
1114 rc = -EBUSY;
1115 }
1116
1117rotator_start_exit:
1118 mutex_unlock(&msm_rotator_dev->rotator_lock);
1119
1120 return rc;
1121}
1122
1123static int msm_rotator_finish(unsigned long arg)
1124{
1125 int rc = 0;
1126 int s;
1127 unsigned int session_id;
1128
1129 if (copy_from_user(&session_id, (void __user *)arg, sizeof(s)))
1130 return -EFAULT;
1131
1132 mutex_lock(&msm_rotator_dev->rotator_lock);
1133 for (s = 0; s < MAX_SESSIONS; s++) {
1134 if ((msm_rotator_dev->img_info[s] != NULL) &&
1135 (session_id ==
1136 (unsigned int)msm_rotator_dev->img_info[s])) {
1137 if (msm_rotator_dev->last_session_idx == s)
1138 msm_rotator_dev->last_session_idx =
1139 INVALID_SESSION;
1140 kfree(msm_rotator_dev->img_info[s]);
1141 msm_rotator_dev->img_info[s] = NULL;
1142 msm_rotator_dev->pid_list[s] = 0;
1143 break;
1144 }
1145 }
1146
1147 if (s == MAX_SESSIONS)
1148 rc = -EINVAL;
1149 mutex_unlock(&msm_rotator_dev->rotator_lock);
1150 return rc;
1151}
1152
1153static int
1154msm_rotator_open(struct inode *inode, struct file *filp)
1155{
1156 int *id;
1157 int i;
1158
1159 if (filp->private_data)
1160 return -EBUSY;
1161
1162 mutex_lock(&msm_rotator_dev->rotator_lock);
1163 id = &msm_rotator_dev->pid_list[0];
1164 for (i = 0; i < MAX_SESSIONS; i++, id++) {
1165 if (*id == 0)
1166 break;
1167 }
1168 mutex_unlock(&msm_rotator_dev->rotator_lock);
1169
1170 if (i == MAX_SESSIONS)
1171 return -EBUSY;
1172
1173 filp->private_data = (void *)task_tgid_nr(current);
1174
1175 return 0;
1176}
1177
1178static int
1179msm_rotator_close(struct inode *inode, struct file *filp)
1180{
1181 int s;
1182 int pid;
1183
1184 pid = (int)filp->private_data;
1185 mutex_lock(&msm_rotator_dev->rotator_lock);
1186 for (s = 0; s < MAX_SESSIONS; s++) {
1187 if (msm_rotator_dev->img_info[s] != NULL &&
1188 msm_rotator_dev->pid_list[s] == pid) {
1189 kfree(msm_rotator_dev->img_info[s]);
1190 msm_rotator_dev->img_info[s] = NULL;
1191 if (msm_rotator_dev->last_session_idx == s)
1192 msm_rotator_dev->last_session_idx =
1193 INVALID_SESSION;
1194 }
1195 }
1196 mutex_unlock(&msm_rotator_dev->rotator_lock);
1197
1198 return 0;
1199}
1200
1201static long msm_rotator_ioctl(struct file *file, unsigned cmd,
1202 unsigned long arg)
1203{
1204 int pid;
1205
1206 if (_IOC_TYPE(cmd) != MSM_ROTATOR_IOCTL_MAGIC)
1207 return -ENOTTY;
1208
1209 pid = (int)file->private_data;
1210
1211 switch (cmd) {
1212 case MSM_ROTATOR_IOCTL_START:
1213 return msm_rotator_start(arg, pid);
1214 case MSM_ROTATOR_IOCTL_ROTATE:
1215 return msm_rotator_do_rotate(arg);
1216 case MSM_ROTATOR_IOCTL_FINISH:
1217 return msm_rotator_finish(arg);
1218
1219 default:
1220 dev_dbg(msm_rotator_dev->device,
1221 "unexpected IOCTL %d\n", cmd);
1222 return -ENOTTY;
1223 }
1224}
1225
1226static const struct file_operations msm_rotator_fops = {
1227 .owner = THIS_MODULE,
1228 .open = msm_rotator_open,
1229 .release = msm_rotator_close,
1230 .unlocked_ioctl = msm_rotator_ioctl,
1231};
1232
1233static int __devinit msm_rotator_probe(struct platform_device *pdev)
1234{
1235 int rc = 0;
1236 struct resource *res;
1237 struct msm_rotator_platform_data *pdata = NULL;
1238 int i, number_of_clks;
1239 uint32_t ver;
1240
1241 msm_rotator_dev = kzalloc(sizeof(struct msm_rotator_dev), GFP_KERNEL);
1242 if (!msm_rotator_dev) {
1243 printk(KERN_ERR "%s Unable to allocate memory for struct\n",
1244 __func__);
1245 return -ENOMEM;
1246 }
1247 for (i = 0; i < MAX_SESSIONS; i++)
1248 msm_rotator_dev->img_info[i] = NULL;
1249 msm_rotator_dev->last_session_idx = INVALID_SESSION;
1250
1251 pdata = pdev->dev.platform_data;
1252 number_of_clks = pdata->number_of_clocks;
1253
1254 msm_rotator_dev->imem_owner = IMEM_NO_OWNER;
1255 mutex_init(&msm_rotator_dev->imem_lock);
1256 msm_rotator_dev->imem_clk_state = CLK_DIS;
1257 INIT_DELAYED_WORK(&msm_rotator_dev->imem_clk_work,
1258 msm_rotator_imem_clk_work_f);
1259 msm_rotator_dev->imem_clk = NULL;
1260 msm_rotator_dev->pdev = pdev;
1261
1262 msm_rotator_dev->core_clk = NULL;
1263 msm_rotator_dev->pclk = NULL;
1264 msm_rotator_dev->axi_clk = NULL;
1265
1266 for (i = 0; i < number_of_clks; i++) {
1267 if (pdata->rotator_clks[i].clk_type == ROTATOR_IMEM_CLK) {
1268 msm_rotator_dev->imem_clk =
1269 clk_get(&msm_rotator_dev->pdev->dev,
1270 pdata->rotator_clks[i].clk_name);
1271 if (IS_ERR(msm_rotator_dev->imem_clk)) {
1272 rc = PTR_ERR(msm_rotator_dev->imem_clk);
1273 msm_rotator_dev->imem_clk = NULL;
1274 printk(KERN_ERR "%s: cannot get imem_clk "
1275 "rc=%d\n", DRIVER_NAME, rc);
1276 goto error_imem_clk;
1277 }
1278 if (pdata->rotator_clks[i].clk_rate)
1279 clk_set_min_rate(msm_rotator_dev->imem_clk,
1280 pdata->rotator_clks[i].clk_rate);
1281 }
1282 if (pdata->rotator_clks[i].clk_type == ROTATOR_PCLK) {
1283 msm_rotator_dev->pclk =
1284 clk_get(&msm_rotator_dev->pdev->dev,
1285 pdata->rotator_clks[i].clk_name);
1286 if (IS_ERR(msm_rotator_dev->pclk)) {
1287 rc = PTR_ERR(msm_rotator_dev->pclk);
1288 msm_rotator_dev->pclk = NULL;
1289 printk(KERN_ERR "%s: cannot get pclk rc=%d\n",
1290 DRIVER_NAME, rc);
1291 goto error_pclk;
1292 }
1293
1294 if (pdata->rotator_clks[i].clk_rate)
1295 clk_set_min_rate(msm_rotator_dev->pclk,
1296 pdata->rotator_clks[i].clk_rate);
1297 }
1298
1299 if (pdata->rotator_clks[i].clk_type == ROTATOR_CORE_CLK) {
1300 msm_rotator_dev->core_clk =
1301 clk_get(&msm_rotator_dev->pdev->dev,
1302 pdata->rotator_clks[i].clk_name);
1303 if (IS_ERR(msm_rotator_dev->core_clk)) {
1304 rc = PTR_ERR(msm_rotator_dev->core_clk);
1305 msm_rotator_dev->core_clk = NULL;
1306 printk(KERN_ERR "%s: cannot get core clk "
1307 "rc=%d\n", DRIVER_NAME, rc);
1308 goto error_core_clk;
1309 }
1310
1311 if (pdata->rotator_clks[i].clk_rate)
1312 clk_set_min_rate(msm_rotator_dev->core_clk,
1313 pdata->rotator_clks[i].clk_rate);
1314 }
1315
1316 if (pdata->rotator_clks[i].clk_type == ROTATOR_AXI_CLK) {
1317 msm_rotator_dev->axi_clk =
1318 clk_get(&msm_rotator_dev->pdev->dev,
1319 pdata->rotator_clks[i].clk_name);
1320 if (IS_ERR(msm_rotator_dev->axi_clk)) {
1321 rc = PTR_ERR(msm_rotator_dev->axi_clk);
1322 msm_rotator_dev->axi_clk = NULL;
1323 printk(KERN_ERR "%s: cannot get axi clk "
1324 "rc=%d\n", DRIVER_NAME, rc);
1325 goto error_axi_clk;
1326 }
1327
1328 if (pdata->rotator_clks[i].clk_rate)
1329 clk_set_min_rate(msm_rotator_dev->axi_clk,
1330 pdata->rotator_clks[i].clk_rate);
1331 }
1332 }
1333
1334 msm_rotator_dev->regulator = regulator_get(NULL, pdata->regulator_name);
1335 if (IS_ERR(msm_rotator_dev->regulator))
1336 msm_rotator_dev->regulator = NULL;
1337
1338 msm_rotator_dev->rot_clk_state = CLK_DIS;
1339 INIT_DELAYED_WORK(&msm_rotator_dev->rot_clk_work,
1340 msm_rotator_rot_clk_work_f);
1341
1342 mutex_init(&msm_rotator_dev->rotator_lock);
1343
1344 platform_set_drvdata(pdev, msm_rotator_dev);
1345
1346
1347 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1348 if (!res) {
1349 printk(KERN_ALERT
1350 "%s: could not get IORESOURCE_MEM\n", DRIVER_NAME);
1351 rc = -ENODEV;
1352 goto error_get_resource;
1353 }
1354 msm_rotator_dev->io_base = ioremap(res->start,
1355 resource_size(res));
1356
1357#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
1358 if (msm_rotator_dev->imem_clk)
1359 clk_enable(msm_rotator_dev->imem_clk);
1360#endif
1361 enable_rot_clks();
1362 ver = ioread32(MSM_ROTATOR_HW_VERSION);
1363 disable_rot_clks();
1364
1365#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
1366 if (msm_rotator_dev->imem_clk)
1367 clk_disable(msm_rotator_dev->imem_clk);
1368#endif
1369 if (ver != pdata->hardware_version_number) {
1370 printk(KERN_ALERT "%s: invalid HW version\n", DRIVER_NAME);
1371 rc = -ENODEV;
1372 goto error_get_resource;
1373 }
1374 msm_rotator_dev->irq = platform_get_irq(pdev, 0);
1375 if (msm_rotator_dev->irq < 0) {
1376 printk(KERN_ALERT "%s: could not get IORESOURCE_IRQ\n",
1377 DRIVER_NAME);
1378 rc = -ENODEV;
1379 goto error_get_irq;
1380 }
1381 rc = request_irq(msm_rotator_dev->irq, msm_rotator_isr,
1382 IRQF_TRIGGER_RISING, DRIVER_NAME, NULL);
1383 if (rc) {
1384 printk(KERN_ERR "%s: request_irq() failed\n", DRIVER_NAME);
1385 goto error_get_irq;
1386 }
1387 /* we enable the IRQ when we need it in the ioctl */
1388 disable_irq(msm_rotator_dev->irq);
1389
1390 rc = alloc_chrdev_region(&msm_rotator_dev->dev_num, 0, 1, DRIVER_NAME);
1391 if (rc < 0) {
1392 printk(KERN_ERR "%s: alloc_chrdev_region Failed rc = %d\n",
1393 __func__, rc);
1394 goto error_get_irq;
1395 }
1396
1397 msm_rotator_dev->class = class_create(THIS_MODULE, DRIVER_NAME);
1398 if (IS_ERR(msm_rotator_dev->class)) {
1399 rc = PTR_ERR(msm_rotator_dev->class);
1400 printk(KERN_ERR "%s: couldn't create class rc = %d\n",
1401 DRIVER_NAME, rc);
1402 goto error_class_create;
1403 }
1404
1405 msm_rotator_dev->device = device_create(msm_rotator_dev->class, NULL,
1406 msm_rotator_dev->dev_num, NULL,
1407 DRIVER_NAME);
1408 if (IS_ERR(msm_rotator_dev->device)) {
1409 rc = PTR_ERR(msm_rotator_dev->device);
1410 printk(KERN_ERR "%s: device_create failed %d\n",
1411 DRIVER_NAME, rc);
1412 goto error_class_device_create;
1413 }
1414
1415 cdev_init(&msm_rotator_dev->cdev, &msm_rotator_fops);
1416 rc = cdev_add(&msm_rotator_dev->cdev,
1417 MKDEV(MAJOR(msm_rotator_dev->dev_num), 0),
1418 1);
1419 if (rc < 0) {
1420 printk(KERN_ERR "%s: cdev_add failed %d\n", __func__, rc);
1421 goto error_cdev_add;
1422 }
1423
1424 init_waitqueue_head(&msm_rotator_dev->wq);
1425
1426 dev_dbg(msm_rotator_dev->device, "probe successful\n");
1427 return rc;
1428
1429error_cdev_add:
1430 device_destroy(msm_rotator_dev->class, msm_rotator_dev->dev_num);
1431error_class_device_create:
1432 class_destroy(msm_rotator_dev->class);
1433error_class_create:
1434 unregister_chrdev_region(msm_rotator_dev->dev_num, 1);
1435error_get_irq:
1436 iounmap(msm_rotator_dev->io_base);
1437error_get_resource:
1438 mutex_destroy(&msm_rotator_dev->rotator_lock);
1439 if (msm_rotator_dev->regulator)
1440 regulator_put(msm_rotator_dev->regulator);
1441 clk_put(msm_rotator_dev->axi_clk);
1442error_axi_clk:
1443 clk_put(msm_rotator_dev->core_clk);
1444error_core_clk:
1445 clk_put(msm_rotator_dev->pclk);
1446error_pclk:
1447 if (msm_rotator_dev->imem_clk)
1448 clk_put(msm_rotator_dev->imem_clk);
1449error_imem_clk:
1450 mutex_destroy(&msm_rotator_dev->imem_lock);
1451 kfree(msm_rotator_dev);
1452 return rc;
1453}
1454
1455static int __devexit msm_rotator_remove(struct platform_device *plat_dev)
1456{
1457 int i;
1458
1459 free_irq(msm_rotator_dev->irq, NULL);
1460 mutex_destroy(&msm_rotator_dev->rotator_lock);
1461 cdev_del(&msm_rotator_dev->cdev);
1462 device_destroy(msm_rotator_dev->class, msm_rotator_dev->dev_num);
1463 class_destroy(msm_rotator_dev->class);
1464 unregister_chrdev_region(msm_rotator_dev->dev_num, 1);
1465 iounmap(msm_rotator_dev->io_base);
1466 if (msm_rotator_dev->imem_clk) {
1467 if (msm_rotator_dev->imem_clk_state == CLK_EN)
1468 clk_disable(msm_rotator_dev->imem_clk);
1469 clk_put(msm_rotator_dev->imem_clk);
1470 msm_rotator_dev->imem_clk = NULL;
1471 }
1472 if (msm_rotator_dev->rot_clk_state == CLK_EN)
1473 disable_rot_clks();
1474 clk_put(msm_rotator_dev->core_clk);
1475 clk_put(msm_rotator_dev->pclk);
1476 clk_put(msm_rotator_dev->axi_clk);
1477 if (msm_rotator_dev->regulator)
1478 regulator_put(msm_rotator_dev->regulator);
1479 msm_rotator_dev->core_clk = NULL;
1480 msm_rotator_dev->pclk = NULL;
1481 msm_rotator_dev->axi_clk = NULL;
1482 mutex_destroy(&msm_rotator_dev->imem_lock);
1483 for (i = 0; i < MAX_SESSIONS; i++)
1484 if (msm_rotator_dev->img_info[i] != NULL)
1485 kfree(msm_rotator_dev->img_info[i]);
1486 kfree(msm_rotator_dev);
1487 return 0;
1488}
1489
1490#ifdef CONFIG_PM
1491static int msm_rotator_suspend(struct platform_device *dev, pm_message_t state)
1492{
1493 mutex_lock(&msm_rotator_dev->imem_lock);
1494 if (msm_rotator_dev->imem_clk_state == CLK_EN
1495 && msm_rotator_dev->imem_clk) {
1496 clk_disable(msm_rotator_dev->imem_clk);
1497 msm_rotator_dev->imem_clk_state = CLK_SUSPEND;
1498 }
1499 mutex_unlock(&msm_rotator_dev->imem_lock);
1500 mutex_lock(&msm_rotator_dev->rotator_lock);
1501 if (msm_rotator_dev->rot_clk_state == CLK_EN) {
1502 disable_rot_clks();
1503 msm_rotator_dev->rot_clk_state = CLK_SUSPEND;
1504 }
1505 mutex_unlock(&msm_rotator_dev->rotator_lock);
1506 return 0;
1507}
1508
1509static int msm_rotator_resume(struct platform_device *dev)
1510{
1511 mutex_lock(&msm_rotator_dev->imem_lock);
1512 if (msm_rotator_dev->imem_clk_state == CLK_SUSPEND
1513 && msm_rotator_dev->imem_clk) {
1514 clk_enable(msm_rotator_dev->imem_clk);
1515 msm_rotator_dev->imem_clk_state = CLK_EN;
1516 }
1517 mutex_unlock(&msm_rotator_dev->imem_lock);
1518 mutex_lock(&msm_rotator_dev->rotator_lock);
1519 if (msm_rotator_dev->rot_clk_state == CLK_SUSPEND) {
1520 enable_rot_clks();
1521 msm_rotator_dev->rot_clk_state = CLK_EN;
1522 }
1523 mutex_unlock(&msm_rotator_dev->rotator_lock);
1524 return 0;
1525}
1526#endif
1527
1528static struct platform_driver msm_rotator_platform_driver = {
1529 .probe = msm_rotator_probe,
1530 .remove = __devexit_p(msm_rotator_remove),
1531#ifdef CONFIG_PM
1532 .suspend = msm_rotator_suspend,
1533 .resume = msm_rotator_resume,
1534#endif
1535 .driver = {
1536 .owner = THIS_MODULE,
1537 .name = DRIVER_NAME
1538 }
1539};
1540
1541static int __init msm_rotator_init(void)
1542{
1543 return platform_driver_register(&msm_rotator_platform_driver);
1544}
1545
1546static void __exit msm_rotator_exit(void)
1547{
1548 return platform_driver_unregister(&msm_rotator_platform_driver);
1549}
1550
1551module_init(msm_rotator_init);
1552module_exit(msm_rotator_exit);
1553
1554MODULE_DESCRIPTION("MSM Offline Image Rotator driver");
1555MODULE_VERSION("1.0");
1556MODULE_LICENSE("GPL v2");