vpss.c revision d31c100250bb07b6d317e1cfc44614b45a16154a
1/* 2 * Copyright (C) 2009 Texas Instruments. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 * 18 * common vpss system module platform driver for all video drivers. 19 */ 20#include <linux/kernel.h> 21#include <linux/sched.h> 22#include <linux/init.h> 23#include <linux/module.h> 24#include <linux/platform_device.h> 25#include <linux/spinlock.h> 26#include <linux/compiler.h> 27#include <linux/io.h> 28#include <mach/hardware.h> 29#include <media/davinci/vpss.h> 30 31MODULE_LICENSE("GPL"); 32MODULE_DESCRIPTION("VPSS Driver"); 33MODULE_AUTHOR("Texas Instruments"); 34 35/* DM644x defines */ 36#define DM644X_SBL_PCR_VPSS (4) 37 38#define DM355_VPSSBL_INTSEL 0x10 39#define DM355_VPSSBL_EVTSEL 0x14 40/* vpss BL register offsets */ 41#define DM355_VPSSBL_CCDCMUX 0x1c 42/* vpss CLK register offsets */ 43#define DM355_VPSSCLK_CLKCTRL 0x04 44/* masks and shifts */ 45#define VPSS_HSSISEL_SHIFT 4 46/* 47 * VDINT0 - vpss_int0, VDINT1 - vpss_int1, H3A - vpss_int4, 48 * IPIPE_INT1_SDR - vpss_int5 49 */ 50#define DM355_VPSSBL_INTSEL_DEFAULT 0xff83ff10 51/* VENCINT - vpss_int8 */ 52#define DM355_VPSSBL_EVTSEL_DEFAULT 0x4 53 54#define DM365_ISP5_PCCR 0x04 55#define DM365_ISP5_PCCR_BL_CLK_ENABLE BIT(0) 56#define DM365_ISP5_PCCR_ISIF_CLK_ENABLE BIT(1) 57#define DM365_ISP5_PCCR_H3A_CLK_ENABLE BIT(2) 58#define DM365_ISP5_PCCR_RSZ_CLK_ENABLE BIT(3) 59#define DM365_ISP5_PCCR_IPIPE_CLK_ENABLE BIT(4) 60#define DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE BIT(5) 61#define DM365_ISP5_PCCR_RSV BIT(6) 62 63#define DM365_ISP5_BCR 0x08 64#define DM365_ISP5_BCR_ISIF_OUT_ENABLE BIT(1) 65 66#define DM365_ISP5_INTSEL1 0x10 67#define DM365_ISP5_INTSEL2 0x14 68#define DM365_ISP5_INTSEL3 0x18 69#define DM365_ISP5_CCDCMUX 0x20 70#define DM365_ISP5_PG_FRAME_SIZE 0x28 71#define DM365_VPBE_CLK_CTRL 0x00 72 73#define VPSS_CLK_CTRL 0x01c40044 74#define VPSS_CLK_CTRL_VENCCLKEN BIT(3) 75#define VPSS_CLK_CTRL_DACCLKEN BIT(4) 76 77/* 78 * vpss interrupts. VDINT0 - vpss_int0, VDINT1 - vpss_int1, 79 * AF - vpss_int3 80 */ 81#define DM365_ISP5_INTSEL1_DEFAULT 0x0b1f0100 82/* AEW - vpss_int6, RSZ_INT_DMA - vpss_int5 */ 83#define DM365_ISP5_INTSEL2_DEFAULT 0x1f0a0f1f 84/* VENC - vpss_int8 */ 85#define DM365_ISP5_INTSEL3_DEFAULT 0x00000015 86 87/* masks and shifts for DM365*/ 88#define DM365_CCDC_PG_VD_POL_SHIFT 0 89#define DM365_CCDC_PG_HD_POL_SHIFT 1 90 91#define CCD_SRC_SEL_MASK (BIT_MASK(5) | BIT_MASK(4)) 92#define CCD_SRC_SEL_SHIFT 4 93 94/* Different SoC platforms supported by this driver */ 95enum vpss_platform_type { 96 DM644X, 97 DM355, 98 DM365, 99}; 100 101/* 102 * vpss operations. Depends on platform. Not all functions are available 103 * on all platforms. The api, first check if a functio is available before 104 * invoking it. In the probe, the function ptrs are initialized based on 105 * vpss name. vpss name can be "dm355_vpss", "dm644x_vpss" etc. 106 */ 107struct vpss_hw_ops { 108 /* enable clock */ 109 int (*enable_clock)(enum vpss_clock_sel clock_sel, int en); 110 /* select input to ccdc */ 111 void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel); 112 /* clear wbl overflow bit */ 113 int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel); 114 /* set sync polarity */ 115 void (*set_sync_pol)(struct vpss_sync_pol); 116 /* set the PG_FRAME_SIZE register*/ 117 void (*set_pg_frame_size)(struct vpss_pg_frame_size); 118 /* check and clear interrupt if occured */ 119 int (*dma_complete_interrupt)(void); 120}; 121 122/* vpss configuration */ 123struct vpss_oper_config { 124 __iomem void *vpss_regs_base0; 125 __iomem void *vpss_regs_base1; 126 resource_size_t *vpss_regs_base2; 127 enum vpss_platform_type platform; 128 spinlock_t vpss_lock; 129 struct vpss_hw_ops hw_ops; 130}; 131 132static struct vpss_oper_config oper_cfg; 133 134/* register access routines */ 135static inline u32 bl_regr(u32 offset) 136{ 137 return __raw_readl(oper_cfg.vpss_regs_base0 + offset); 138} 139 140static inline void bl_regw(u32 val, u32 offset) 141{ 142 __raw_writel(val, oper_cfg.vpss_regs_base0 + offset); 143} 144 145static inline u32 vpss_regr(u32 offset) 146{ 147 return __raw_readl(oper_cfg.vpss_regs_base1 + offset); 148} 149 150static inline void vpss_regw(u32 val, u32 offset) 151{ 152 __raw_writel(val, oper_cfg.vpss_regs_base1 + offset); 153} 154 155/* For DM365 only */ 156static inline u32 isp5_read(u32 offset) 157{ 158 return __raw_readl(oper_cfg.vpss_regs_base0 + offset); 159} 160 161/* For DM365 only */ 162static inline void isp5_write(u32 val, u32 offset) 163{ 164 __raw_writel(val, oper_cfg.vpss_regs_base0 + offset); 165} 166 167static void dm365_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) 168{ 169 u32 temp = isp5_read(DM365_ISP5_CCDCMUX) & ~CCD_SRC_SEL_MASK; 170 171 /* if we are using pattern generator, enable it */ 172 if (src_sel == VPSS_PGLPBK || src_sel == VPSS_CCDCPG) 173 temp |= 0x08; 174 175 temp |= (src_sel << CCD_SRC_SEL_SHIFT); 176 isp5_write(temp, DM365_ISP5_CCDCMUX); 177} 178 179static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) 180{ 181 bl_regw(src_sel << VPSS_HSSISEL_SHIFT, DM355_VPSSBL_CCDCMUX); 182} 183 184int vpss_dma_complete_interrupt(void) 185{ 186 if (!oper_cfg.hw_ops.dma_complete_interrupt) 187 return 2; 188 return oper_cfg.hw_ops.dma_complete_interrupt(); 189} 190EXPORT_SYMBOL(vpss_dma_complete_interrupt); 191 192int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) 193{ 194 if (!oper_cfg.hw_ops.select_ccdc_source) 195 return -EINVAL; 196 197 oper_cfg.hw_ops.select_ccdc_source(src_sel); 198 return 0; 199} 200EXPORT_SYMBOL(vpss_select_ccdc_source); 201 202static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) 203{ 204 u32 mask = 1, val; 205 206 if (wbl_sel < VPSS_PCR_AEW_WBL_0 || 207 wbl_sel > VPSS_PCR_CCDC_WBL_O) 208 return -EINVAL; 209 210 /* writing a 0 clear the overflow */ 211 mask = ~(mask << wbl_sel); 212 val = bl_regr(DM644X_SBL_PCR_VPSS) & mask; 213 bl_regw(val, DM644X_SBL_PCR_VPSS); 214 return 0; 215} 216 217void vpss_set_sync_pol(struct vpss_sync_pol sync) 218{ 219 if (!oper_cfg.hw_ops.set_sync_pol) 220 return; 221 222 oper_cfg.hw_ops.set_sync_pol(sync); 223} 224EXPORT_SYMBOL(vpss_set_sync_pol); 225 226int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) 227{ 228 if (!oper_cfg.hw_ops.clear_wbl_overflow) 229 return -EINVAL; 230 231 return oper_cfg.hw_ops.clear_wbl_overflow(wbl_sel); 232} 233EXPORT_SYMBOL(vpss_clear_wbl_overflow); 234 235/* 236 * dm355_enable_clock - Enable VPSS Clock 237 * @clock_sel: CLock to be enabled/disabled 238 * @en: enable/disable flag 239 * 240 * This is called to enable or disable a vpss clock 241 */ 242static int dm355_enable_clock(enum vpss_clock_sel clock_sel, int en) 243{ 244 unsigned long flags; 245 u32 utemp, mask = 0x1, shift = 0; 246 247 switch (clock_sel) { 248 case VPSS_VPBE_CLOCK: 249 /* nothing since lsb */ 250 break; 251 case VPSS_VENC_CLOCK_SEL: 252 shift = 2; 253 break; 254 case VPSS_CFALD_CLOCK: 255 shift = 3; 256 break; 257 case VPSS_H3A_CLOCK: 258 shift = 4; 259 break; 260 case VPSS_IPIPE_CLOCK: 261 shift = 5; 262 break; 263 case VPSS_CCDC_CLOCK: 264 shift = 6; 265 break; 266 default: 267 printk(KERN_ERR "dm355_enable_clock:" 268 " Invalid selector: %d\n", clock_sel); 269 return -EINVAL; 270 } 271 272 spin_lock_irqsave(&oper_cfg.vpss_lock, flags); 273 utemp = vpss_regr(DM355_VPSSCLK_CLKCTRL); 274 if (!en) 275 utemp &= ~(mask << shift); 276 else 277 utemp |= (mask << shift); 278 279 vpss_regw(utemp, DM355_VPSSCLK_CLKCTRL); 280 spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags); 281 return 0; 282} 283 284static int dm365_enable_clock(enum vpss_clock_sel clock_sel, int en) 285{ 286 unsigned long flags; 287 u32 utemp, mask = 0x1, shift = 0, offset = DM365_ISP5_PCCR; 288 u32 (*read)(u32 offset) = isp5_read; 289 void(*write)(u32 val, u32 offset) = isp5_write; 290 291 switch (clock_sel) { 292 case VPSS_BL_CLOCK: 293 break; 294 case VPSS_CCDC_CLOCK: 295 shift = 1; 296 break; 297 case VPSS_H3A_CLOCK: 298 shift = 2; 299 break; 300 case VPSS_RSZ_CLOCK: 301 shift = 3; 302 break; 303 case VPSS_IPIPE_CLOCK: 304 shift = 4; 305 break; 306 case VPSS_IPIPEIF_CLOCK: 307 shift = 5; 308 break; 309 case VPSS_PCLK_INTERNAL: 310 shift = 6; 311 break; 312 case VPSS_PSYNC_CLOCK_SEL: 313 shift = 7; 314 break; 315 case VPSS_VPBE_CLOCK: 316 read = vpss_regr; 317 write = vpss_regw; 318 offset = DM365_VPBE_CLK_CTRL; 319 break; 320 case VPSS_VENC_CLOCK_SEL: 321 shift = 2; 322 read = vpss_regr; 323 write = vpss_regw; 324 offset = DM365_VPBE_CLK_CTRL; 325 break; 326 case VPSS_LDC_CLOCK: 327 shift = 3; 328 read = vpss_regr; 329 write = vpss_regw; 330 offset = DM365_VPBE_CLK_CTRL; 331 break; 332 case VPSS_FDIF_CLOCK: 333 shift = 4; 334 read = vpss_regr; 335 write = vpss_regw; 336 offset = DM365_VPBE_CLK_CTRL; 337 break; 338 case VPSS_OSD_CLOCK_SEL: 339 shift = 6; 340 read = vpss_regr; 341 write = vpss_regw; 342 offset = DM365_VPBE_CLK_CTRL; 343 break; 344 case VPSS_LDC_CLOCK_SEL: 345 shift = 7; 346 read = vpss_regr; 347 write = vpss_regw; 348 offset = DM365_VPBE_CLK_CTRL; 349 break; 350 default: 351 printk(KERN_ERR "dm365_enable_clock: Invalid selector: %d\n", 352 clock_sel); 353 return -1; 354 } 355 356 spin_lock_irqsave(&oper_cfg.vpss_lock, flags); 357 utemp = read(offset); 358 if (!en) { 359 mask = ~mask; 360 utemp &= (mask << shift); 361 } else 362 utemp |= (mask << shift); 363 364 write(utemp, offset); 365 spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags); 366 367 return 0; 368} 369 370int vpss_enable_clock(enum vpss_clock_sel clock_sel, int en) 371{ 372 if (!oper_cfg.hw_ops.enable_clock) 373 return -EINVAL; 374 375 return oper_cfg.hw_ops.enable_clock(clock_sel, en); 376} 377EXPORT_SYMBOL(vpss_enable_clock); 378 379void dm365_vpss_set_sync_pol(struct vpss_sync_pol sync) 380{ 381 int val = 0; 382 val = isp5_read(DM365_ISP5_CCDCMUX); 383 384 val |= (sync.ccdpg_hdpol << DM365_CCDC_PG_HD_POL_SHIFT); 385 val |= (sync.ccdpg_vdpol << DM365_CCDC_PG_VD_POL_SHIFT); 386 387 isp5_write(val, DM365_ISP5_CCDCMUX); 388} 389EXPORT_SYMBOL(dm365_vpss_set_sync_pol); 390 391void vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size) 392{ 393 if (!oper_cfg.hw_ops.set_pg_frame_size) 394 return; 395 396 oper_cfg.hw_ops.set_pg_frame_size(frame_size); 397} 398EXPORT_SYMBOL(vpss_set_pg_frame_size); 399 400void dm365_vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size) 401{ 402 int current_reg = ((frame_size.hlpfr >> 1) - 1) << 16; 403 404 current_reg |= (frame_size.pplen - 1); 405 isp5_write(current_reg, DM365_ISP5_PG_FRAME_SIZE); 406} 407EXPORT_SYMBOL(dm365_vpss_set_pg_frame_size); 408 409static int __devinit vpss_probe(struct platform_device *pdev) 410{ 411 struct resource *r1, *r2; 412 char *platform_name; 413 int status; 414 415 if (!pdev->dev.platform_data) { 416 dev_err(&pdev->dev, "no platform data\n"); 417 return -ENOENT; 418 } 419 420 platform_name = pdev->dev.platform_data; 421 if (!strcmp(platform_name, "dm355_vpss")) 422 oper_cfg.platform = DM355; 423 else if (!strcmp(platform_name, "dm365_vpss")) 424 oper_cfg.platform = DM365; 425 else if (!strcmp(platform_name, "dm644x_vpss")) 426 oper_cfg.platform = DM644X; 427 else { 428 dev_err(&pdev->dev, "vpss driver not supported on" 429 " this platform\n"); 430 return -ENODEV; 431 } 432 433 dev_info(&pdev->dev, "%s vpss probed\n", platform_name); 434 r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); 435 if (!r1) 436 return -ENOENT; 437 438 r1 = request_mem_region(r1->start, resource_size(r1), r1->name); 439 if (!r1) 440 return -EBUSY; 441 442 oper_cfg.vpss_regs_base0 = ioremap(r1->start, resource_size(r1)); 443 if (!oper_cfg.vpss_regs_base0) { 444 status = -EBUSY; 445 goto fail1; 446 } 447 448 if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) { 449 r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); 450 if (!r2) { 451 status = -ENOENT; 452 goto fail2; 453 } 454 r2 = request_mem_region(r2->start, resource_size(r2), r2->name); 455 if (!r2) { 456 status = -EBUSY; 457 goto fail2; 458 } 459 460 oper_cfg.vpss_regs_base1 = ioremap(r2->start, 461 resource_size(r2)); 462 if (!oper_cfg.vpss_regs_base1) { 463 status = -EBUSY; 464 goto fail3; 465 } 466 } 467 468 if (oper_cfg.platform == DM355) { 469 oper_cfg.hw_ops.enable_clock = dm355_enable_clock; 470 oper_cfg.hw_ops.select_ccdc_source = dm355_select_ccdc_source; 471 /* Setup vpss interrupts */ 472 bl_regw(DM355_VPSSBL_INTSEL_DEFAULT, DM355_VPSSBL_INTSEL); 473 bl_regw(DM355_VPSSBL_EVTSEL_DEFAULT, DM355_VPSSBL_EVTSEL); 474 } else if (oper_cfg.platform == DM365) { 475 oper_cfg.hw_ops.enable_clock = dm365_enable_clock; 476 oper_cfg.hw_ops.select_ccdc_source = dm365_select_ccdc_source; 477 /* Setup vpss interrupts */ 478 isp5_write((isp5_read(DM365_ISP5_PCCR) | 479 DM365_ISP5_PCCR_BL_CLK_ENABLE | 480 DM365_ISP5_PCCR_ISIF_CLK_ENABLE | 481 DM365_ISP5_PCCR_H3A_CLK_ENABLE | 482 DM365_ISP5_PCCR_RSZ_CLK_ENABLE | 483 DM365_ISP5_PCCR_IPIPE_CLK_ENABLE | 484 DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE | 485 DM365_ISP5_PCCR_RSV), DM365_ISP5_PCCR); 486 isp5_write((isp5_read(DM365_ISP5_BCR) | 487 DM365_ISP5_BCR_ISIF_OUT_ENABLE), DM365_ISP5_BCR); 488 isp5_write(DM365_ISP5_INTSEL1_DEFAULT, DM365_ISP5_INTSEL1); 489 isp5_write(DM365_ISP5_INTSEL2_DEFAULT, DM365_ISP5_INTSEL2); 490 isp5_write(DM365_ISP5_INTSEL3_DEFAULT, DM365_ISP5_INTSEL3); 491 } else 492 oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow; 493 494 spin_lock_init(&oper_cfg.vpss_lock); 495 dev_info(&pdev->dev, "%s vpss probe success\n", platform_name); 496 return 0; 497 498fail3: 499 release_mem_region(r2->start, resource_size(r2)); 500fail2: 501 iounmap(oper_cfg.vpss_regs_base0); 502fail1: 503 release_mem_region(r1->start, resource_size(r1)); 504 return status; 505} 506 507static int __devexit vpss_remove(struct platform_device *pdev) 508{ 509 struct resource *res; 510 511 iounmap(oper_cfg.vpss_regs_base0); 512 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 513 release_mem_region(res->start, resource_size(res)); 514 if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) { 515 iounmap(oper_cfg.vpss_regs_base1); 516 res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 517 release_mem_region(res->start, resource_size(res)); 518 } 519 return 0; 520} 521 522static struct platform_driver vpss_driver = { 523 .driver = { 524 .name = "vpss", 525 .owner = THIS_MODULE, 526 }, 527 .remove = __devexit_p(vpss_remove), 528 .probe = vpss_probe, 529}; 530 531static void vpss_exit(void) 532{ 533 iounmap(oper_cfg.vpss_regs_base2); 534 release_mem_region(VPSS_CLK_CTRL, 4); 535 platform_driver_unregister(&vpss_driver); 536} 537 538static int __init vpss_init(void) 539{ 540 if (!request_mem_region(VPSS_CLK_CTRL, 4, "vpss_clock_control")) 541 return -EBUSY; 542 543 oper_cfg.vpss_regs_base2 = ioremap(VPSS_CLK_CTRL, 4); 544 writel(VPSS_CLK_CTRL_VENCCLKEN | 545 VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2); 546 547 return platform_driver_register(&vpss_driver); 548} 549subsys_initcall(vpss_init); 550module_exit(vpss_exit); 551