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