vpss.c (13108B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (C) 2009 Texas Instruments. 4 * 5 * common vpss system module platform driver for all video drivers. 6 */ 7#include <linux/module.h> 8#include <linux/platform_device.h> 9#include <linux/io.h> 10#include <linux/pm_runtime.h> 11#include <linux/err.h> 12 13#include <media/davinci/vpss.h> 14 15MODULE_LICENSE("GPL"); 16MODULE_DESCRIPTION("VPSS Driver"); 17MODULE_AUTHOR("Texas Instruments"); 18 19/* DM644x defines */ 20#define DM644X_SBL_PCR_VPSS (4) 21 22#define DM355_VPSSBL_INTSEL 0x10 23#define DM355_VPSSBL_EVTSEL 0x14 24/* vpss BL register offsets */ 25#define DM355_VPSSBL_CCDCMUX 0x1c 26/* vpss CLK register offsets */ 27#define DM355_VPSSCLK_CLKCTRL 0x04 28/* masks and shifts */ 29#define VPSS_HSSISEL_SHIFT 4 30/* 31 * VDINT0 - vpss_int0, VDINT1 - vpss_int1, H3A - vpss_int4, 32 * IPIPE_INT1_SDR - vpss_int5 33 */ 34#define DM355_VPSSBL_INTSEL_DEFAULT 0xff83ff10 35/* VENCINT - vpss_int8 */ 36#define DM355_VPSSBL_EVTSEL_DEFAULT 0x4 37 38#define DM365_ISP5_PCCR 0x04 39#define DM365_ISP5_PCCR_BL_CLK_ENABLE BIT(0) 40#define DM365_ISP5_PCCR_ISIF_CLK_ENABLE BIT(1) 41#define DM365_ISP5_PCCR_H3A_CLK_ENABLE BIT(2) 42#define DM365_ISP5_PCCR_RSZ_CLK_ENABLE BIT(3) 43#define DM365_ISP5_PCCR_IPIPE_CLK_ENABLE BIT(4) 44#define DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE BIT(5) 45#define DM365_ISP5_PCCR_RSV BIT(6) 46 47#define DM365_ISP5_BCR 0x08 48#define DM365_ISP5_BCR_ISIF_OUT_ENABLE BIT(1) 49 50#define DM365_ISP5_INTSEL1 0x10 51#define DM365_ISP5_INTSEL2 0x14 52#define DM365_ISP5_INTSEL3 0x18 53#define DM365_ISP5_CCDCMUX 0x20 54#define DM365_ISP5_PG_FRAME_SIZE 0x28 55#define DM365_VPBE_CLK_CTRL 0x00 56 57#define VPSS_CLK_CTRL 0x01c40044 58#define VPSS_CLK_CTRL_VENCCLKEN BIT(3) 59#define VPSS_CLK_CTRL_DACCLKEN BIT(4) 60 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 function 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 /* set sync polarity */ 99 void (*set_sync_pol)(struct vpss_sync_pol); 100 /* set the PG_FRAME_SIZE register*/ 101 void (*set_pg_frame_size)(struct vpss_pg_frame_size); 102 /* check and clear interrupt if occurred */ 103 int (*dma_complete_interrupt)(void); 104}; 105 106/* vpss configuration */ 107struct vpss_oper_config { 108 __iomem void *vpss_regs_base0; 109 __iomem void *vpss_regs_base1; 110 __iomem void *vpss_regs_base2; 111 enum vpss_platform_type platform; 112 spinlock_t vpss_lock; 113 struct vpss_hw_ops hw_ops; 114}; 115 116static struct vpss_oper_config oper_cfg; 117 118/* register access routines */ 119static inline u32 bl_regr(u32 offset) 120{ 121 return __raw_readl(oper_cfg.vpss_regs_base0 + offset); 122} 123 124static inline void bl_regw(u32 val, u32 offset) 125{ 126 __raw_writel(val, oper_cfg.vpss_regs_base0 + offset); 127} 128 129static inline u32 vpss_regr(u32 offset) 130{ 131 return __raw_readl(oper_cfg.vpss_regs_base1 + offset); 132} 133 134static inline void vpss_regw(u32 val, u32 offset) 135{ 136 __raw_writel(val, oper_cfg.vpss_regs_base1 + offset); 137} 138 139/* For DM365 only */ 140static inline u32 isp5_read(u32 offset) 141{ 142 return __raw_readl(oper_cfg.vpss_regs_base0 + offset); 143} 144 145/* For DM365 only */ 146static inline void isp5_write(u32 val, u32 offset) 147{ 148 __raw_writel(val, oper_cfg.vpss_regs_base0 + offset); 149} 150 151static void dm365_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) 152{ 153 u32 temp = isp5_read(DM365_ISP5_CCDCMUX) & ~CCD_SRC_SEL_MASK; 154 155 /* if we are using pattern generator, enable it */ 156 if (src_sel == VPSS_PGLPBK || src_sel == VPSS_CCDCPG) 157 temp |= 0x08; 158 159 temp |= (src_sel << CCD_SRC_SEL_SHIFT); 160 isp5_write(temp, DM365_ISP5_CCDCMUX); 161} 162 163static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) 164{ 165 bl_regw(src_sel << VPSS_HSSISEL_SHIFT, DM355_VPSSBL_CCDCMUX); 166} 167 168int vpss_dma_complete_interrupt(void) 169{ 170 if (!oper_cfg.hw_ops.dma_complete_interrupt) 171 return 2; 172 return oper_cfg.hw_ops.dma_complete_interrupt(); 173} 174EXPORT_SYMBOL(vpss_dma_complete_interrupt); 175 176int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) 177{ 178 if (!oper_cfg.hw_ops.select_ccdc_source) 179 return -EINVAL; 180 181 oper_cfg.hw_ops.select_ccdc_source(src_sel); 182 return 0; 183} 184EXPORT_SYMBOL(vpss_select_ccdc_source); 185 186static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) 187{ 188 u32 mask = 1, val; 189 190 if (wbl_sel < VPSS_PCR_AEW_WBL_0 || 191 wbl_sel > VPSS_PCR_CCDC_WBL_O) 192 return -EINVAL; 193 194 /* writing a 0 clear the overflow */ 195 mask = ~(mask << wbl_sel); 196 val = bl_regr(DM644X_SBL_PCR_VPSS) & mask; 197 bl_regw(val, DM644X_SBL_PCR_VPSS); 198 return 0; 199} 200 201void vpss_set_sync_pol(struct vpss_sync_pol sync) 202{ 203 if (!oper_cfg.hw_ops.set_sync_pol) 204 return; 205 206 oper_cfg.hw_ops.set_sync_pol(sync); 207} 208EXPORT_SYMBOL(vpss_set_sync_pol); 209 210int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) 211{ 212 if (!oper_cfg.hw_ops.clear_wbl_overflow) 213 return -EINVAL; 214 215 return oper_cfg.hw_ops.clear_wbl_overflow(wbl_sel); 216} 217EXPORT_SYMBOL(vpss_clear_wbl_overflow); 218 219/* 220 * dm355_enable_clock - Enable VPSS Clock 221 * @clock_sel: Clock to be enabled/disabled 222 * @en: enable/disable flag 223 * 224 * This is called to enable or disable a vpss clock 225 */ 226static int dm355_enable_clock(enum vpss_clock_sel clock_sel, int en) 227{ 228 unsigned long flags; 229 u32 utemp, mask = 0x1, shift = 0; 230 231 switch (clock_sel) { 232 case VPSS_VPBE_CLOCK: 233 /* nothing since lsb */ 234 break; 235 case VPSS_VENC_CLOCK_SEL: 236 shift = 2; 237 break; 238 case VPSS_CFALD_CLOCK: 239 shift = 3; 240 break; 241 case VPSS_H3A_CLOCK: 242 shift = 4; 243 break; 244 case VPSS_IPIPE_CLOCK: 245 shift = 5; 246 break; 247 case VPSS_CCDC_CLOCK: 248 shift = 6; 249 break; 250 default: 251 printk(KERN_ERR "dm355_enable_clock: Invalid selector: %d\n", 252 clock_sel); 253 return -EINVAL; 254 } 255 256 spin_lock_irqsave(&oper_cfg.vpss_lock, flags); 257 utemp = vpss_regr(DM355_VPSSCLK_CLKCTRL); 258 if (!en) 259 utemp &= ~(mask << shift); 260 else 261 utemp |= (mask << shift); 262 263 vpss_regw(utemp, DM355_VPSSCLK_CLKCTRL); 264 spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags); 265 return 0; 266} 267 268static int dm365_enable_clock(enum vpss_clock_sel clock_sel, int en) 269{ 270 unsigned long flags; 271 u32 utemp, mask = 0x1, shift = 0, offset = DM365_ISP5_PCCR; 272 u32 (*read)(u32 offset) = isp5_read; 273 void(*write)(u32 val, u32 offset) = isp5_write; 274 275 switch (clock_sel) { 276 case VPSS_BL_CLOCK: 277 break; 278 case VPSS_CCDC_CLOCK: 279 shift = 1; 280 break; 281 case VPSS_H3A_CLOCK: 282 shift = 2; 283 break; 284 case VPSS_RSZ_CLOCK: 285 shift = 3; 286 break; 287 case VPSS_IPIPE_CLOCK: 288 shift = 4; 289 break; 290 case VPSS_IPIPEIF_CLOCK: 291 shift = 5; 292 break; 293 case VPSS_PCLK_INTERNAL: 294 shift = 6; 295 break; 296 case VPSS_PSYNC_CLOCK_SEL: 297 shift = 7; 298 break; 299 case VPSS_VPBE_CLOCK: 300 read = vpss_regr; 301 write = vpss_regw; 302 offset = DM365_VPBE_CLK_CTRL; 303 break; 304 case VPSS_VENC_CLOCK_SEL: 305 shift = 2; 306 read = vpss_regr; 307 write = vpss_regw; 308 offset = DM365_VPBE_CLK_CTRL; 309 break; 310 case VPSS_LDC_CLOCK: 311 shift = 3; 312 read = vpss_regr; 313 write = vpss_regw; 314 offset = DM365_VPBE_CLK_CTRL; 315 break; 316 case VPSS_FDIF_CLOCK: 317 shift = 4; 318 read = vpss_regr; 319 write = vpss_regw; 320 offset = DM365_VPBE_CLK_CTRL; 321 break; 322 case VPSS_OSD_CLOCK_SEL: 323 shift = 6; 324 read = vpss_regr; 325 write = vpss_regw; 326 offset = DM365_VPBE_CLK_CTRL; 327 break; 328 case VPSS_LDC_CLOCK_SEL: 329 shift = 7; 330 read = vpss_regr; 331 write = vpss_regw; 332 offset = DM365_VPBE_CLK_CTRL; 333 break; 334 default: 335 printk(KERN_ERR "dm365_enable_clock: Invalid selector: %d\n", 336 clock_sel); 337 return -1; 338 } 339 340 spin_lock_irqsave(&oper_cfg.vpss_lock, flags); 341 utemp = read(offset); 342 if (!en) { 343 mask = ~mask; 344 utemp &= (mask << shift); 345 } else 346 utemp |= (mask << shift); 347 348 write(utemp, offset); 349 spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags); 350 351 return 0; 352} 353 354int vpss_enable_clock(enum vpss_clock_sel clock_sel, int en) 355{ 356 if (!oper_cfg.hw_ops.enable_clock) 357 return -EINVAL; 358 359 return oper_cfg.hw_ops.enable_clock(clock_sel, en); 360} 361EXPORT_SYMBOL(vpss_enable_clock); 362 363void dm365_vpss_set_sync_pol(struct vpss_sync_pol sync) 364{ 365 int val = 0; 366 val = isp5_read(DM365_ISP5_CCDCMUX); 367 368 val |= (sync.ccdpg_hdpol << DM365_CCDC_PG_HD_POL_SHIFT); 369 val |= (sync.ccdpg_vdpol << DM365_CCDC_PG_VD_POL_SHIFT); 370 371 isp5_write(val, DM365_ISP5_CCDCMUX); 372} 373EXPORT_SYMBOL(dm365_vpss_set_sync_pol); 374 375void vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size) 376{ 377 if (!oper_cfg.hw_ops.set_pg_frame_size) 378 return; 379 380 oper_cfg.hw_ops.set_pg_frame_size(frame_size); 381} 382EXPORT_SYMBOL(vpss_set_pg_frame_size); 383 384void dm365_vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size) 385{ 386 int current_reg = ((frame_size.hlpfr >> 1) - 1) << 16; 387 388 current_reg |= (frame_size.pplen - 1); 389 isp5_write(current_reg, DM365_ISP5_PG_FRAME_SIZE); 390} 391EXPORT_SYMBOL(dm365_vpss_set_pg_frame_size); 392 393static int vpss_probe(struct platform_device *pdev) 394{ 395 char *platform_name; 396 397 if (!pdev->dev.platform_data) { 398 dev_err(&pdev->dev, "no platform data\n"); 399 return -ENOENT; 400 } 401 402 platform_name = pdev->dev.platform_data; 403 if (!strcmp(platform_name, "dm355_vpss")) 404 oper_cfg.platform = DM355; 405 else if (!strcmp(platform_name, "dm365_vpss")) 406 oper_cfg.platform = DM365; 407 else if (!strcmp(platform_name, "dm644x_vpss")) 408 oper_cfg.platform = DM644X; 409 else { 410 dev_err(&pdev->dev, "vpss driver not supported on this platform\n"); 411 return -ENODEV; 412 } 413 414 dev_info(&pdev->dev, "%s vpss probed\n", platform_name); 415 oper_cfg.vpss_regs_base0 = devm_platform_ioremap_resource(pdev, 0); 416 if (IS_ERR(oper_cfg.vpss_regs_base0)) 417 return PTR_ERR(oper_cfg.vpss_regs_base0); 418 419 if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) { 420 oper_cfg.vpss_regs_base1 = devm_platform_ioremap_resource(pdev, 1); 421 if (IS_ERR(oper_cfg.vpss_regs_base1)) 422 return PTR_ERR(oper_cfg.vpss_regs_base1); 423 } 424 425 if (oper_cfg.platform == DM355) { 426 oper_cfg.hw_ops.enable_clock = dm355_enable_clock; 427 oper_cfg.hw_ops.select_ccdc_source = dm355_select_ccdc_source; 428 /* Setup vpss interrupts */ 429 bl_regw(DM355_VPSSBL_INTSEL_DEFAULT, DM355_VPSSBL_INTSEL); 430 bl_regw(DM355_VPSSBL_EVTSEL_DEFAULT, DM355_VPSSBL_EVTSEL); 431 } else if (oper_cfg.platform == DM365) { 432 oper_cfg.hw_ops.enable_clock = dm365_enable_clock; 433 oper_cfg.hw_ops.select_ccdc_source = dm365_select_ccdc_source; 434 /* Setup vpss interrupts */ 435 isp5_write((isp5_read(DM365_ISP5_PCCR) | 436 DM365_ISP5_PCCR_BL_CLK_ENABLE | 437 DM365_ISP5_PCCR_ISIF_CLK_ENABLE | 438 DM365_ISP5_PCCR_H3A_CLK_ENABLE | 439 DM365_ISP5_PCCR_RSZ_CLK_ENABLE | 440 DM365_ISP5_PCCR_IPIPE_CLK_ENABLE | 441 DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE | 442 DM365_ISP5_PCCR_RSV), DM365_ISP5_PCCR); 443 isp5_write((isp5_read(DM365_ISP5_BCR) | 444 DM365_ISP5_BCR_ISIF_OUT_ENABLE), DM365_ISP5_BCR); 445 isp5_write(DM365_ISP5_INTSEL1_DEFAULT, DM365_ISP5_INTSEL1); 446 isp5_write(DM365_ISP5_INTSEL2_DEFAULT, DM365_ISP5_INTSEL2); 447 isp5_write(DM365_ISP5_INTSEL3_DEFAULT, DM365_ISP5_INTSEL3); 448 } else 449 oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow; 450 451 pm_runtime_enable(&pdev->dev); 452 453 pm_runtime_get(&pdev->dev); 454 455 spin_lock_init(&oper_cfg.vpss_lock); 456 dev_info(&pdev->dev, "%s vpss probe success\n", platform_name); 457 458 return 0; 459} 460 461static int vpss_remove(struct platform_device *pdev) 462{ 463 pm_runtime_disable(&pdev->dev); 464 return 0; 465} 466 467static int vpss_suspend(struct device *dev) 468{ 469 pm_runtime_put(dev); 470 return 0; 471} 472 473static int vpss_resume(struct device *dev) 474{ 475 pm_runtime_get(dev); 476 return 0; 477} 478 479static const struct dev_pm_ops vpss_pm_ops = { 480 .suspend = vpss_suspend, 481 .resume = vpss_resume, 482}; 483 484static struct platform_driver vpss_driver = { 485 .driver = { 486 .name = "vpss", 487 .pm = &vpss_pm_ops, 488 }, 489 .remove = vpss_remove, 490 .probe = vpss_probe, 491}; 492 493static void vpss_exit(void) 494{ 495 platform_driver_unregister(&vpss_driver); 496 iounmap(oper_cfg.vpss_regs_base2); 497 release_mem_region(VPSS_CLK_CTRL, 4); 498} 499 500static int __init vpss_init(void) 501{ 502 int ret; 503 504 if (!request_mem_region(VPSS_CLK_CTRL, 4, "vpss_clock_control")) 505 return -EBUSY; 506 507 oper_cfg.vpss_regs_base2 = ioremap(VPSS_CLK_CTRL, 4); 508 if (unlikely(!oper_cfg.vpss_regs_base2)) { 509 ret = -ENOMEM; 510 goto err_ioremap; 511 } 512 513 writel(VPSS_CLK_CTRL_VENCCLKEN | 514 VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2); 515 516 ret = platform_driver_register(&vpss_driver); 517 if (ret) 518 goto err_pd_register; 519 520 return 0; 521 522err_pd_register: 523 iounmap(oper_cfg.vpss_regs_base2); 524err_ioremap: 525 release_mem_region(VPSS_CLK_CTRL, 4); 526 return ret; 527} 528subsys_initcall(vpss_init); 529module_exit(vpss_exit);