vpss.c revision 2c3fb08b3f74b8792004095a1f6881a3296ff643
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_INTSEL1 0x10 56#define DM365_ISP5_INTSEL2 0x14 57#define DM365_ISP5_INTSEL3 0x18 58#define DM365_ISP5_CCDCMUX 0x20 59#define DM365_ISP5_PG_FRAME_SIZE 0x28 60#define DM365_VPBE_CLK_CTRL 0x00 61/* 62 * vpss interrupts. VDINT0 - vpss_int0, VDINT1 - vpss_int1, 63 * AF - vpss_int3 64 */ 65#define DM365_ISP5_INTSEL1_DEFAULT 0x0b1f0100 66/* AEW - vpss_int6, RSZ_INT_DMA - vpss_int5 */ 67#define DM365_ISP5_INTSEL2_DEFAULT 0x1f0a0f1f 68/* VENC - vpss_int8 */ 69#define DM365_ISP5_INTSEL3_DEFAULT 0x00000015 70 71/* masks and shifts for DM365*/ 72#define DM365_CCDC_PG_VD_POL_SHIFT 0 73#define DM365_CCDC_PG_HD_POL_SHIFT 1 74 75#define CCD_SRC_SEL_MASK (BIT_MASK(5) | BIT_MASK(4)) 76#define CCD_SRC_SEL_SHIFT 4 77 78/* Different SoC platforms supported by this driver */ 79enum vpss_platform_type { 80 DM644X, 81 DM355, 82 DM365, 83}; 84 85/* 86 * vpss operations. Depends on platform. Not all functions are available 87 * on all platforms. The api, first check if a functio is available before 88 * invoking it. In the probe, the function ptrs are initialized based on 89 * vpss name. vpss name can be "dm355_vpss", "dm644x_vpss" etc. 90 */ 91struct vpss_hw_ops { 92 /* enable clock */ 93 int (*enable_clock)(enum vpss_clock_sel clock_sel, int en); 94 /* select input to ccdc */ 95 void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel); 96 /* clear wbl overflow bit */ 97 int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel); 98}; 99 100/* vpss configuration */ 101struct vpss_oper_config { 102 __iomem void *vpss_regs_base0; 103 __iomem void *vpss_regs_base1; 104 enum vpss_platform_type platform; 105 spinlock_t vpss_lock; 106 struct vpss_hw_ops hw_ops; 107}; 108 109static struct vpss_oper_config oper_cfg; 110 111/* register access routines */ 112static inline u32 bl_regr(u32 offset) 113{ 114 return __raw_readl(oper_cfg.vpss_regs_base0 + offset); 115} 116 117static inline void bl_regw(u32 val, u32 offset) 118{ 119 __raw_writel(val, oper_cfg.vpss_regs_base0 + offset); 120} 121 122static inline u32 vpss_regr(u32 offset) 123{ 124 return __raw_readl(oper_cfg.vpss_regs_base1 + offset); 125} 126 127static inline void vpss_regw(u32 val, u32 offset) 128{ 129 __raw_writel(val, oper_cfg.vpss_regs_base1 + offset); 130} 131 132/* For DM365 only */ 133static inline u32 isp5_read(u32 offset) 134{ 135 return __raw_readl(oper_cfg.vpss_regs_base0 + offset); 136} 137 138/* For DM365 only */ 139static inline void isp5_write(u32 val, u32 offset) 140{ 141 __raw_writel(val, oper_cfg.vpss_regs_base0 + offset); 142} 143 144static void dm365_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) 145{ 146 u32 temp = isp5_read(DM365_ISP5_CCDCMUX) & ~CCD_SRC_SEL_MASK; 147 148 /* if we are using pattern generator, enable it */ 149 if (src_sel == VPSS_PGLPBK || src_sel == VPSS_CCDCPG) 150 temp |= 0x08; 151 152 temp |= (src_sel << CCD_SRC_SEL_SHIFT); 153 isp5_write(temp, DM365_ISP5_CCDCMUX); 154} 155 156static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) 157{ 158 bl_regw(src_sel << VPSS_HSSISEL_SHIFT, DM355_VPSSBL_CCDCMUX); 159} 160 161int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) 162{ 163 if (!oper_cfg.hw_ops.select_ccdc_source) 164 return -EINVAL; 165 166 oper_cfg.hw_ops.select_ccdc_source(src_sel); 167 return 0; 168} 169EXPORT_SYMBOL(vpss_select_ccdc_source); 170 171static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) 172{ 173 u32 mask = 1, val; 174 175 if (wbl_sel < VPSS_PCR_AEW_WBL_0 || 176 wbl_sel > VPSS_PCR_CCDC_WBL_O) 177 return -EINVAL; 178 179 /* writing a 0 clear the overflow */ 180 mask = ~(mask << wbl_sel); 181 val = bl_regr(DM644X_SBL_PCR_VPSS) & mask; 182 bl_regw(val, DM644X_SBL_PCR_VPSS); 183 return 0; 184} 185 186int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) 187{ 188 if (!oper_cfg.hw_ops.clear_wbl_overflow) 189 return -EINVAL; 190 191 return oper_cfg.hw_ops.clear_wbl_overflow(wbl_sel); 192} 193EXPORT_SYMBOL(vpss_clear_wbl_overflow); 194 195/* 196 * dm355_enable_clock - Enable VPSS Clock 197 * @clock_sel: CLock to be enabled/disabled 198 * @en: enable/disable flag 199 * 200 * This is called to enable or disable a vpss clock 201 */ 202static int dm355_enable_clock(enum vpss_clock_sel clock_sel, int en) 203{ 204 unsigned long flags; 205 u32 utemp, mask = 0x1, shift = 0; 206 207 switch (clock_sel) { 208 case VPSS_VPBE_CLOCK: 209 /* nothing since lsb */ 210 break; 211 case VPSS_VENC_CLOCK_SEL: 212 shift = 2; 213 break; 214 case VPSS_CFALD_CLOCK: 215 shift = 3; 216 break; 217 case VPSS_H3A_CLOCK: 218 shift = 4; 219 break; 220 case VPSS_IPIPE_CLOCK: 221 shift = 5; 222 break; 223 case VPSS_CCDC_CLOCK: 224 shift = 6; 225 break; 226 default: 227 printk(KERN_ERR "dm355_enable_clock:" 228 " Invalid selector: %d\n", clock_sel); 229 return -EINVAL; 230 } 231 232 spin_lock_irqsave(&oper_cfg.vpss_lock, flags); 233 utemp = vpss_regr(DM355_VPSSCLK_CLKCTRL); 234 if (!en) 235 utemp &= ~(mask << shift); 236 else 237 utemp |= (mask << shift); 238 239 vpss_regw(utemp, DM355_VPSSCLK_CLKCTRL); 240 spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags); 241 return 0; 242} 243 244static int dm365_enable_clock(enum vpss_clock_sel clock_sel, int en) 245{ 246 unsigned long flags; 247 u32 utemp, mask = 0x1, shift = 0, offset = DM365_ISP5_PCCR; 248 u32 (*read)(u32 offset) = isp5_read; 249 void(*write)(u32 val, u32 offset) = isp5_write; 250 251 switch (clock_sel) { 252 case VPSS_BL_CLOCK: 253 break; 254 case VPSS_CCDC_CLOCK: 255 shift = 1; 256 break; 257 case VPSS_H3A_CLOCK: 258 shift = 2; 259 break; 260 case VPSS_RSZ_CLOCK: 261 shift = 3; 262 break; 263 case VPSS_IPIPE_CLOCK: 264 shift = 4; 265 break; 266 case VPSS_IPIPEIF_CLOCK: 267 shift = 5; 268 break; 269 case VPSS_PCLK_INTERNAL: 270 shift = 6; 271 break; 272 case VPSS_PSYNC_CLOCK_SEL: 273 shift = 7; 274 break; 275 case VPSS_VPBE_CLOCK: 276 read = vpss_regr; 277 write = vpss_regw; 278 offset = DM365_VPBE_CLK_CTRL; 279 break; 280 case VPSS_VENC_CLOCK_SEL: 281 shift = 2; 282 read = vpss_regr; 283 write = vpss_regw; 284 offset = DM365_VPBE_CLK_CTRL; 285 break; 286 case VPSS_LDC_CLOCK: 287 shift = 3; 288 read = vpss_regr; 289 write = vpss_regw; 290 offset = DM365_VPBE_CLK_CTRL; 291 break; 292 case VPSS_FDIF_CLOCK: 293 shift = 4; 294 read = vpss_regr; 295 write = vpss_regw; 296 offset = DM365_VPBE_CLK_CTRL; 297 break; 298 case VPSS_OSD_CLOCK_SEL: 299 shift = 6; 300 read = vpss_regr; 301 write = vpss_regw; 302 offset = DM365_VPBE_CLK_CTRL; 303 break; 304 case VPSS_LDC_CLOCK_SEL: 305 shift = 7; 306 read = vpss_regr; 307 write = vpss_regw; 308 offset = DM365_VPBE_CLK_CTRL; 309 break; 310 default: 311 printk(KERN_ERR "dm365_enable_clock: Invalid selector: %d\n", 312 clock_sel); 313 return -1; 314 } 315 316 spin_lock_irqsave(&oper_cfg.vpss_lock, flags); 317 utemp = read(offset); 318 if (!en) { 319 mask = ~mask; 320 utemp &= (mask << shift); 321 } else 322 utemp |= (mask << shift); 323 324 write(utemp, offset); 325 spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags); 326 327 return 0; 328} 329 330int vpss_enable_clock(enum vpss_clock_sel clock_sel, int en) 331{ 332 if (!oper_cfg.hw_ops.enable_clock) 333 return -EINVAL; 334 335 return oper_cfg.hw_ops.enable_clock(clock_sel, en); 336} 337EXPORT_SYMBOL(vpss_enable_clock); 338 339void dm365_vpss_set_sync_pol(struct vpss_sync_pol sync) 340{ 341 int val = 0; 342 val = isp5_read(DM365_ISP5_CCDCMUX); 343 344 val |= (sync.ccdpg_hdpol << DM365_CCDC_PG_HD_POL_SHIFT); 345 val |= (sync.ccdpg_vdpol << DM365_CCDC_PG_VD_POL_SHIFT); 346 347 isp5_write(val, DM365_ISP5_CCDCMUX); 348} 349EXPORT_SYMBOL(dm365_vpss_set_sync_pol); 350 351void dm365_vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size) 352{ 353 int current_reg = ((frame_size.hlpfr >> 1) - 1) << 16; 354 355 current_reg |= (frame_size.pplen - 1); 356 isp5_write(current_reg, DM365_ISP5_PG_FRAME_SIZE); 357} 358EXPORT_SYMBOL(dm365_vpss_set_pg_frame_size); 359 360static int __devinit vpss_probe(struct platform_device *pdev) 361{ 362 struct resource *r1, *r2; 363 char *platform_name; 364 int status; 365 366 if (!pdev->dev.platform_data) { 367 dev_err(&pdev->dev, "no platform data\n"); 368 return -ENOENT; 369 } 370 371 platform_name = pdev->dev.platform_data; 372 if (!strcmp(platform_name, "dm355_vpss")) 373 oper_cfg.platform = DM355; 374 else if (!strcmp(platform_name, "dm365_vpss")) 375 oper_cfg.platform = DM365; 376 else if (!strcmp(platform_name, "dm644x_vpss")) 377 oper_cfg.platform = DM644X; 378 else { 379 dev_err(&pdev->dev, "vpss driver not supported on" 380 " this platform\n"); 381 return -ENODEV; 382 } 383 384 dev_info(&pdev->dev, "%s vpss probed\n", platform_name); 385 r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); 386 if (!r1) 387 return -ENOENT; 388 389 r1 = request_mem_region(r1->start, resource_size(r1), r1->name); 390 if (!r1) 391 return -EBUSY; 392 393 oper_cfg.vpss_regs_base0 = ioremap(r1->start, resource_size(r1)); 394 if (!oper_cfg.vpss_regs_base0) { 395 status = -EBUSY; 396 goto fail1; 397 } 398 399 if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) { 400 r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); 401 if (!r2) { 402 status = -ENOENT; 403 goto fail2; 404 } 405 r2 = request_mem_region(r2->start, resource_size(r2), r2->name); 406 if (!r2) { 407 status = -EBUSY; 408 goto fail2; 409 } 410 411 oper_cfg.vpss_regs_base1 = ioremap(r2->start, 412 resource_size(r2)); 413 if (!oper_cfg.vpss_regs_base1) { 414 status = -EBUSY; 415 goto fail3; 416 } 417 } 418 419 if (oper_cfg.platform == DM355) { 420 oper_cfg.hw_ops.enable_clock = dm355_enable_clock; 421 oper_cfg.hw_ops.select_ccdc_source = dm355_select_ccdc_source; 422 /* Setup vpss interrupts */ 423 bl_regw(DM355_VPSSBL_INTSEL_DEFAULT, DM355_VPSSBL_INTSEL); 424 bl_regw(DM355_VPSSBL_EVTSEL_DEFAULT, DM355_VPSSBL_EVTSEL); 425 } else if (oper_cfg.platform == DM365) { 426 oper_cfg.hw_ops.enable_clock = dm365_enable_clock; 427 oper_cfg.hw_ops.select_ccdc_source = dm365_select_ccdc_source; 428 /* Setup vpss interrupts */ 429 isp5_write(DM365_ISP5_INTSEL1_DEFAULT, DM365_ISP5_INTSEL1); 430 isp5_write(DM365_ISP5_INTSEL2_DEFAULT, DM365_ISP5_INTSEL2); 431 isp5_write(DM365_ISP5_INTSEL3_DEFAULT, DM365_ISP5_INTSEL3); 432 } else 433 oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow; 434 435 spin_lock_init(&oper_cfg.vpss_lock); 436 dev_info(&pdev->dev, "%s vpss probe success\n", platform_name); 437 return 0; 438 439fail3: 440 release_mem_region(r2->start, resource_size(r2)); 441fail2: 442 iounmap(oper_cfg.vpss_regs_base0); 443fail1: 444 release_mem_region(r1->start, resource_size(r1)); 445 return status; 446} 447 448static int __devexit vpss_remove(struct platform_device *pdev) 449{ 450 struct resource *res; 451 452 iounmap(oper_cfg.vpss_regs_base0); 453 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 454 release_mem_region(res->start, resource_size(res)); 455 if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) { 456 iounmap(oper_cfg.vpss_regs_base1); 457 res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 458 release_mem_region(res->start, resource_size(res)); 459 } 460 return 0; 461} 462 463static struct platform_driver vpss_driver = { 464 .driver = { 465 .name = "vpss", 466 .owner = THIS_MODULE, 467 }, 468 .remove = __devexit_p(vpss_remove), 469 .probe = vpss_probe, 470}; 471 472static void vpss_exit(void) 473{ 474 platform_driver_unregister(&vpss_driver); 475} 476 477static int __init vpss_init(void) 478{ 479 return platform_driver_register(&vpss_driver); 480} 481subsys_initcall(vpss_init); 482module_exit(vpss_exit); 483