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