Mercurial > mplayer.hg
comparison vidix/sh_veu_vid.c @ 27420:917c93c652b0
Add VIDIX driver for SuperH Mobile VEU hardware block.
Patch by Magnus Damm <magnus dot damm at gmail dot com>.
author | ben |
---|---|
date | Mon, 11 Aug 2008 23:10:03 +0000 |
parents | |
children | f504a06fcf50 |
comparison
equal
deleted
inserted
replaced
27419:3f6d802c6ea8 | 27420:917c93c652b0 |
---|---|
1 /* | |
2 * VIDIX driver for SuperH Mobile VEU hardware block. | |
3 * Copyright (C) 2008 Magnus Damm | |
4 * | |
5 * Requires a kernel that exposes the VEU hardware block to user space | |
6 * using UIO. Available in upstream linux-2.6.27 or later. | |
7 * | |
8 * Tested using WVGA and QVGA panels with sh7722 VEU and sh7723 VEU2H. | |
9 * | |
10 * This file is part of MPlayer. | |
11 * | |
12 * MPlayer is free software; you can redistribute it and/or modify | |
13 * it under the terms of the GNU General Public License as published by | |
14 * the Free Software Foundation; either version 2 of the License, or | |
15 * (at your option) any later version. | |
16 * | |
17 * MPlayer is distributed in the hope that it will be useful, | |
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 * GNU General Public License for more details. | |
21 * | |
22 * You should have received a copy of the GNU General Public License | |
23 * along with MPlayer; if not, write to the Free Software | |
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
25 */ | |
26 | |
27 #include <stdio.h> | |
28 #include <unistd.h> | |
29 #include <string.h> | |
30 #include <stdlib.h> | |
31 #include <sys/mman.h> | |
32 #include <sys/time.h> | |
33 #include <sys/ioctl.h> | |
34 #include <fcntl.h> | |
35 #include <linux/fb.h> | |
36 #include <inttypes.h> | |
37 #include <unistd.h> | |
38 #include <errno.h> | |
39 | |
40 #include "config.h" | |
41 #include "vidix.h" | |
42 #include "fourcc.h" | |
43 | |
44 #include "dha.h" | |
45 | |
46 static int fgets_with_openclose(char *fname, char *buf, size_t maxlen) | |
47 { | |
48 FILE *fp; | |
49 | |
50 fp = fopen(fname, "r"); | |
51 if (!fp) | |
52 return -1; | |
53 | |
54 fgets(buf, maxlen, fp); | |
55 fclose(fp); | |
56 return strlen(buf); | |
57 } | |
58 | |
59 struct uio_device { | |
60 char *name; | |
61 char *path; | |
62 int fd; | |
63 }; | |
64 | |
65 #define MAXNAMELEN 256 | |
66 | |
67 static int locate_uio_device(char *name, struct uio_device *udp) | |
68 { | |
69 char fname[MAXNAMELEN], buf[MAXNAMELEN]; | |
70 char *tmp; | |
71 int uio_id; | |
72 | |
73 uio_id = -1; | |
74 do { | |
75 uio_id++; | |
76 snprintf(fname, MAXNAMELEN, "/sys/class/uio/uio%d/name", uio_id); | |
77 if (fgets_with_openclose(fname, buf, MAXNAMELEN) < 0) | |
78 return -1; | |
79 | |
80 } while (strncmp(name, buf, strlen(name))); | |
81 | |
82 tmp = strchr(buf, '\n'); | |
83 if (tmp) | |
84 *tmp = '\0'; | |
85 | |
86 udp->name = strdup(buf); | |
87 udp->path = strdup(fname); | |
88 udp->path[strlen(udp->path) - 5] = '\0'; | |
89 | |
90 snprintf(buf, MAXNAMELEN, "/dev/uio%d", uio_id); | |
91 udp->fd = open(buf, O_RDWR | O_SYNC); | |
92 | |
93 if (udp->fd < 0) | |
94 return -1; | |
95 | |
96 return 0; | |
97 } | |
98 | |
99 struct uio_map { | |
100 unsigned long address; | |
101 unsigned long size; | |
102 void *iomem; | |
103 }; | |
104 | |
105 static int setup_uio_map(struct uio_device *udp, int nr, struct uio_map *ump) | |
106 { | |
107 char fname[MAXNAMELEN], buf[MAXNAMELEN]; | |
108 | |
109 snprintf(fname, MAXNAMELEN, "%s/maps/map%d/addr", udp->path, nr); | |
110 if (fgets_with_openclose(fname, buf, MAXNAMELEN) <= 0) | |
111 return -1; | |
112 | |
113 ump->address = strtoul(buf, NULL, 0); | |
114 | |
115 snprintf(fname, MAXNAMELEN, "%s/maps/map%d/size", udp->path, nr); | |
116 if (fgets_with_openclose(fname, buf, MAXNAMELEN) <= 0) | |
117 return -1; | |
118 | |
119 ump->size = strtoul(buf, NULL, 0); | |
120 ump->iomem = mmap(0, ump->size, | |
121 PROT_READ|PROT_WRITE, MAP_SHARED, | |
122 udp->fd, nr * getpagesize()); | |
123 | |
124 if (ump->iomem == MAP_FAILED) | |
125 return -1; | |
126 | |
127 return 0; | |
128 } | |
129 | |
130 struct fb_info { | |
131 unsigned long width; | |
132 unsigned long height; | |
133 unsigned long bpp; | |
134 unsigned long line_length; | |
135 | |
136 unsigned long address; | |
137 unsigned long size; | |
138 }; | |
139 | |
140 static int get_fb_info(char *device, struct fb_info *fip) | |
141 { | |
142 struct fb_var_screeninfo vinfo; | |
143 struct fb_fix_screeninfo finfo; | |
144 void *iomem; | |
145 int fd; | |
146 | |
147 fd = open(device, O_RDWR); | |
148 if (fd < 0) { | |
149 perror("open"); | |
150 return -1; | |
151 } | |
152 | |
153 if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) == -1) { | |
154 perror("ioctl(FBIOGET_VSCREENINFO)"); | |
155 return -1; | |
156 } | |
157 | |
158 fip->width = vinfo.xres; | |
159 fip->height = vinfo.yres; | |
160 fip->bpp = vinfo.bits_per_pixel; | |
161 | |
162 if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) { | |
163 perror("ioctl(FBIOGET_FSCREENINFO)"); | |
164 return -1; | |
165 } | |
166 | |
167 fip->address = finfo.smem_start; | |
168 fip->size = finfo.smem_len; | |
169 fip->line_length = finfo.line_length; | |
170 | |
171 iomem = mmap(0, fip->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); | |
172 | |
173 if (iomem == MAP_FAILED) { | |
174 perror("mmap"); | |
175 return -1; | |
176 } | |
177 | |
178 /* clear framebuffer */ | |
179 memset(iomem, 0, fip->line_length * fip->height); | |
180 munmap(iomem, fip->size); | |
181 | |
182 close(fd); | |
183 return 0; | |
184 } | |
185 | |
186 #define VESTR 0x00 /* start register */ | |
187 #define VESWR 0x10 /* src: line length */ | |
188 #define VESSR 0x14 /* src: image size */ | |
189 #define VSAYR 0x18 /* src: y/rgb plane address */ | |
190 #define VSACR 0x1c /* src: c plane address */ | |
191 #define VBSSR 0x20 /* bundle mode register */ | |
192 #define VEDWR 0x30 /* dst: line length */ | |
193 #define VDAYR 0x34 /* dst: y/rgb plane address */ | |
194 #define VDACR 0x38 /* dst: c plane address */ | |
195 #define VTRCR 0x50 /* transform control */ | |
196 #define VRFCR 0x54 /* resize scale */ | |
197 #define VRFSR 0x58 /* resize clip */ | |
198 #define VENHR 0x5c /* enhance */ | |
199 #define VFMCR 0x70 /* filter mode */ | |
200 #define VVTCR 0x74 /* lowpass vertical */ | |
201 #define VHTCR 0x78 /* lowpass horizontal */ | |
202 #define VAPCR 0x80 /* color match */ | |
203 #define VECCR 0x84 /* color replace */ | |
204 #define VAFXR 0x90 /* fixed mode */ | |
205 #define VSWPR 0x94 /* swap */ | |
206 #define VEIER 0xa0 /* interrupt mask */ | |
207 #define VEVTR 0xa4 /* interrupt event */ | |
208 #define VSTAR 0xb0 /* status */ | |
209 #define VBSRR 0xb4 /* reset */ | |
210 | |
211 #define VMCR00 0x200 /* color conversion matrix coefficient 00 */ | |
212 #define VMCR01 0x204 /* color conversion matrix coefficient 01 */ | |
213 #define VMCR02 0x208 /* color conversion matrix coefficient 02 */ | |
214 #define VMCR10 0x20c /* color conversion matrix coefficient 10 */ | |
215 #define VMCR11 0x210 /* color conversion matrix coefficient 11 */ | |
216 #define VMCR12 0x214 /* color conversion matrix coefficient 12 */ | |
217 #define VMCR20 0x218 /* color conversion matrix coefficient 20 */ | |
218 #define VMCR21 0x21c /* color conversion matrix coefficient 21 */ | |
219 #define VMCR22 0x220 /* color conversion matrix coefficient 22 */ | |
220 #define VCOFFR 0x224 /* color conversion offset */ | |
221 #define VCBR 0x228 /* color conversion clip */ | |
222 | |
223 /* Helper functions for reading registers. */ | |
224 | |
225 static unsigned long read_reg(struct uio_map *ump, int reg_offs) | |
226 { | |
227 volatile unsigned long *reg = ump->iomem; | |
228 | |
229 return reg[reg_offs / 4]; | |
230 } | |
231 | |
232 static void write_reg(struct uio_map *ump, unsigned long value, int reg_offs) | |
233 { | |
234 volatile unsigned long *reg = ump->iomem; | |
235 | |
236 reg[reg_offs / 4] = value; | |
237 } | |
238 | |
239 static vidix_capability_t sh_veu_cap = { | |
240 "SuperH VEU driver", | |
241 "Magnus Damm", | |
242 TYPE_OUTPUT, | |
243 { 0, 0, 0, 0 }, | |
244 2560, | |
245 1920, | |
246 16, | |
247 16, | |
248 -1, | |
249 FLAG_UPSCALER|FLAG_DOWNSCALER, | |
250 -1, | |
251 -1, | |
252 { 0, 0, 0, 0 } | |
253 }; | |
254 | |
255 /* global variables yuck */ | |
256 | |
257 static struct fb_info fbi; | |
258 static struct uio_device uio_dev; | |
259 static struct uio_map uio_mmio, uio_mem; | |
260 | |
261 struct sh_veu_plane { | |
262 unsigned long width; | |
263 unsigned long height; | |
264 unsigned long stride; | |
265 unsigned long pos_x; | |
266 unsigned long pos_y; | |
267 }; | |
268 | |
269 static struct sh_veu_plane _src, _dst; | |
270 static vidix_playback_t my_info; | |
271 | |
272 static int sh_veu_probe(int verbose, int force) | |
273 { | |
274 int ret; | |
275 | |
276 ret = get_fb_info("/dev/fb0", &fbi); | |
277 if (ret < 0) | |
278 return ret; | |
279 | |
280 if (fbi.bpp != 16) { | |
281 printf("sh_veu: only 16bpp supported\n"); | |
282 return -1; | |
283 } | |
284 | |
285 ret = locate_uio_device("VEU", &uio_dev); | |
286 if (ret < 0) { | |
287 printf("sh_veu: unable to locate matching UIO device\n"); | |
288 return ret; | |
289 } | |
290 | |
291 ret = setup_uio_map(&uio_dev, 0, &uio_mmio); | |
292 if (ret < 0) { | |
293 printf("sh_veu: cannot setup MMIO\n"); | |
294 return ret; | |
295 } | |
296 | |
297 ret = setup_uio_map(&uio_dev, 1, &uio_mem); | |
298 if (ret < 0) { | |
299 printf("sh_veu: cannot setup contiguous memory\n"); | |
300 return ret; | |
301 } | |
302 | |
303 printf("sh_veu: Using %s at %s on %lux%lu %ldbpp /dev/fb0\n", | |
304 uio_dev.name, uio_dev.path, | |
305 fbi.width, fbi.height, fbi.bpp); | |
306 | |
307 return ret; | |
308 } | |
309 | |
310 static void sh_veu_wait_irq(vidix_playback_t *info) | |
311 { | |
312 unsigned long n_pending; | |
313 | |
314 /* Wait for an interrupt */ | |
315 read(uio_dev.fd, &n_pending, sizeof(u_long)); | |
316 | |
317 write_reg(&uio_mmio, 0x100, VEVTR); /* ack int, write 0 to bit 0 */ | |
318 } | |
319 | |
320 static int sh_veu_is_veu2h(void) | |
321 { | |
322 return uio_mmio.size > 0xb8; | |
323 } | |
324 | |
325 static unsigned long sh_veu_do_scale(struct uio_map *ump, | |
326 int vertical, int size_in, | |
327 int size_out, int crop_out) | |
328 { | |
329 unsigned long fixpoint, mant, frac, value, rep; | |
330 | |
331 /* calculate FRAC and MANT */ | |
332 do { | |
333 rep = mant = frac = 0; | |
334 | |
335 if (size_in == size_out) { | |
336 if (crop_out != size_out) | |
337 mant = 1; /* needed for cropping */ | |
338 break; | |
339 } | |
340 | |
341 /* VEU2H special upscale */ | |
342 if (sh_veu_is_veu2h() && size_out > size_in) { | |
343 fixpoint = (4096 * size_in) / size_out; | |
344 | |
345 mant = fixpoint / 4096; | |
346 frac = fixpoint - (mant * 4096); | |
347 frac &= ~0x07; | |
348 | |
349 switch (frac) { | |
350 case 0x800: | |
351 rep = 1; | |
352 break; | |
353 case 0x400: | |
354 rep = 3; | |
355 break; | |
356 case 0x200: | |
357 rep = 7; | |
358 break; | |
359 } | |
360 | |
361 if (rep) | |
362 break; | |
363 } | |
364 | |
365 fixpoint = (4096 * (size_in - 1)) / (size_out + 1); | |
366 mant = fixpoint / 4096; | |
367 frac = fixpoint - (mant * 4096); | |
368 | |
369 if (frac & 0x07) { | |
370 frac &= ~0x07; | |
371 if (size_out > size_in) | |
372 frac -= 8; /* round down if scaling up */ | |
373 else | |
374 frac += 8; /* round up if scaling down */ | |
375 } | |
376 | |
377 } while (0); | |
378 | |
379 /* set scale */ | |
380 value = read_reg(ump, VRFCR); | |
381 if (vertical) { | |
382 value &= ~0xffff0000; | |
383 value |= ((mant << 12) | frac) << 16; | |
384 } else { | |
385 value &= ~0xffff; | |
386 value |= (mant << 12) | frac; | |
387 } | |
388 write_reg(ump, value, VRFCR); | |
389 | |
390 /* set clip */ | |
391 value = read_reg(ump, VRFSR); | |
392 if (vertical) { | |
393 value &= ~0xffff0000; | |
394 value |= ((rep << 12) | crop_out) << 16; | |
395 } else { | |
396 value &= ~0xffff; | |
397 value |= (rep << 12) | crop_out; | |
398 } | |
399 write_reg(ump, value, VRFSR); | |
400 | |
401 return (((size_in * crop_out) / size_out) + 0x03) & ~0x03; | |
402 } | |
403 | |
404 static void sh_veu_setup_planes(vidix_playback_t *info, | |
405 struct sh_veu_plane *src, | |
406 struct sh_veu_plane *dst) | |
407 { | |
408 unsigned long addr, real_w, real_h; | |
409 | |
410 src->width = info->src.w; | |
411 src->height = info->src.h; | |
412 src->stride = (info->src.w+15) & ~15; | |
413 | |
414 dst->width = real_w = info->dest.w; | |
415 dst->height = real_h = info->dest.h; | |
416 dst->stride = fbi.line_length; | |
417 dst->pos_x = info->dest.x & ~0x03; | |
418 dst->pos_y = info->dest.y; | |
419 | |
420 if ((dst->width + dst->pos_x) > fbi.width) | |
421 dst->width = fbi.width - dst->pos_x; | |
422 | |
423 if ((dst->height + dst->pos_y) > fbi.height) | |
424 dst->height = fbi.height - dst->pos_y; | |
425 | |
426 addr = fbi.address; | |
427 addr += dst->pos_x * (fbi.bpp / 8); | |
428 addr += dst->pos_y * dst->stride; | |
429 | |
430 src->width = sh_veu_do_scale(&uio_mmio, 0, src->width, | |
431 real_w, dst->width); | |
432 src->height = sh_veu_do_scale(&uio_mmio, 1, src->height, | |
433 real_h, dst->height); | |
434 | |
435 write_reg(&uio_mmio, src->stride, VESWR); | |
436 write_reg(&uio_mmio, src->width | (src->height << 16), VESSR); | |
437 write_reg(&uio_mmio, 0, VBSSR); /* not using bundle mode */ | |
438 | |
439 write_reg(&uio_mmio, dst->stride, VEDWR); | |
440 write_reg(&uio_mmio, addr, VDAYR); | |
441 write_reg(&uio_mmio, 0, VDACR); /* unused for RGB */ | |
442 | |
443 write_reg(&uio_mmio, 0x67, VSWPR); | |
444 write_reg(&uio_mmio, (6 << 16) | (0 << 14) | 2 | 4, VTRCR); | |
445 | |
446 if (sh_veu_is_veu2h()) { | |
447 write_reg(&uio_mmio, 0x0cc5, VMCR00); | |
448 write_reg(&uio_mmio, 0x0950, VMCR01); | |
449 write_reg(&uio_mmio, 0x0000, VMCR02); | |
450 | |
451 write_reg(&uio_mmio, 0x397f, VMCR10); | |
452 write_reg(&uio_mmio, 0x0950, VMCR11); | |
453 write_reg(&uio_mmio, 0x3ccd, VMCR12); | |
454 | |
455 write_reg(&uio_mmio, 0x0000, VMCR20); | |
456 write_reg(&uio_mmio, 0x0950, VMCR21); | |
457 write_reg(&uio_mmio, 0x1023, VMCR22); | |
458 | |
459 write_reg(&uio_mmio, 0x00800010, VCOFFR); | |
460 } | |
461 | |
462 write_reg(&uio_mmio, 1, VEIER); /* enable interrupt in VEU */ | |
463 } | |
464 | |
465 static void sh_veu_blit(vidix_playback_t *info, int frame) | |
466 { | |
467 unsigned long enable = 1; | |
468 unsigned long addr; | |
469 | |
470 addr = uio_mem.address + info->offsets[frame]; | |
471 | |
472 write_reg(&uio_mmio, addr + info->offset.y, VSAYR); | |
473 write_reg(&uio_mmio, addr + info->offset.u, VSACR); | |
474 | |
475 /* Enable interrupt in UIO driver */ | |
476 write(uio_dev.fd, &enable, sizeof(u_long)); | |
477 | |
478 write_reg(&uio_mmio, 1, VESTR); /* start operation */ | |
479 } | |
480 | |
481 static int sh_veu_init(void) | |
482 { | |
483 write_reg(&uio_mmio, 0x100, VBSRR); /* reset VEU */ | |
484 return 0; | |
485 } | |
486 | |
487 static void sh_veu_destroy(void) | |
488 { | |
489 } | |
490 | |
491 static int sh_veu_get_caps(vidix_capability_t *to) | |
492 { | |
493 memcpy(to, &sh_veu_cap, sizeof(vidix_capability_t)); | |
494 return 0; | |
495 } | |
496 | |
497 static int sh_veu_query_fourcc(vidix_fourcc_t *to) | |
498 { | |
499 if (to->fourcc == IMGFMT_NV12) { | |
500 to->depth = VID_DEPTH_ALL; | |
501 to->flags = VID_CAP_EXPAND | VID_CAP_SHRINK; | |
502 return 0; | |
503 } | |
504 to->depth = to->flags = 0; | |
505 return ENOSYS; | |
506 } | |
507 | |
508 | |
509 static int sh_veu_config_playback(vidix_playback_t *info) | |
510 { | |
511 unsigned int i, y_pitch; | |
512 | |
513 switch (info->fourcc) { | |
514 case IMGFMT_NV12: | |
515 y_pitch = (info->src.w + 15) & ~15; | |
516 | |
517 info->offset.y = 0; | |
518 info->offset.u = y_pitch * info->src.h; | |
519 info->frame_size = info->offset.u + info->offset.u / 2; | |
520 break; | |
521 default: | |
522 return ENOTSUP; | |
523 } | |
524 | |
525 info->num_frames = uio_mem.size / info->frame_size; | |
526 if (info->num_frames > VID_PLAY_MAXFRAMES) | |
527 info->num_frames = VID_PLAY_MAXFRAMES; | |
528 | |
529 if (!info->num_frames) { | |
530 printf("sh_veu: %d is not enough memory for %d bytes frame\n", | |
531 (int)uio_mem.size, (int)info->frame_size); | |
532 return ENOMEM; | |
533 } | |
534 | |
535 info->dga_addr = uio_mem.iomem; | |
536 info->dest.pitch.y = info->dest.pitch.u = info->dest.pitch.v = 16; | |
537 | |
538 for (i = 0; i < info->num_frames; i++) | |
539 info->offsets[i] = info->frame_size * i; | |
540 | |
541 my_info = *info; | |
542 | |
543 printf("sh_veu: %d frames * %d bytes, total size = %d\n", | |
544 (int)info->num_frames, (int)info->frame_size, | |
545 (int)uio_mem.size); | |
546 | |
547 sh_veu_setup_planes(info, &_src, &_dst); | |
548 | |
549 printf("sh_veu: %dx%d->%dx%d@%dx%d -> %dx%d->%dx%d@%dx%d \n", | |
550 (int)info->src.w, (int)info->src.h, | |
551 (int)info->dest.w, (int)info->dest.h, | |
552 (int)info->dest.x, (int)info->dest.y, | |
553 (int)_src.width, (int)_src.height, | |
554 (int)_dst.width, (int)_dst.height, | |
555 (int)_dst.pos_x, (int)_dst.pos_y); | |
556 return 0; | |
557 } | |
558 | |
559 | |
560 static int sh_veu_playback_on(void) | |
561 { | |
562 return 0; | |
563 } | |
564 | |
565 static int sh_veu_playback_off(void) | |
566 { | |
567 return 0; | |
568 } | |
569 | |
570 static int sh_veu_first_frame = 1; | |
571 | |
572 static int sh_veu_frame_sel(unsigned int frame) | |
573 { | |
574 if (!sh_veu_first_frame) | |
575 sh_veu_wait_irq(&my_info); | |
576 | |
577 sh_veu_blit(&my_info, frame); | |
578 sh_veu_first_frame = 0; | |
579 return 0; | |
580 } | |
581 | |
582 VDXDriver sh_veu_drv = { | |
583 "sh_veu", | |
584 NULL, | |
585 .probe = sh_veu_probe, | |
586 .get_caps = sh_veu_get_caps, | |
587 .query_fourcc = sh_veu_query_fourcc, | |
588 .init = sh_veu_init, | |
589 .destroy = sh_veu_destroy, | |
590 .config_playback = sh_veu_config_playback, | |
591 .playback_on = sh_veu_playback_on, | |
592 .playback_off = sh_veu_playback_off, | |
593 .frame_sel = sh_veu_frame_sel, | |
594 }; |