linux/drivers/media/platform/davinci/vpss.c
<<
>>
Prefs
   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        struct resource *res;
 396        char *platform_name;
 397
 398        if (!pdev->dev.platform_data) {
 399                dev_err(&pdev->dev, "no platform data\n");
 400                return -ENOENT;
 401        }
 402
 403        platform_name = pdev->dev.platform_data;
 404        if (!strcmp(platform_name, "dm355_vpss"))
 405                oper_cfg.platform = DM355;
 406        else if (!strcmp(platform_name, "dm365_vpss"))
 407                oper_cfg.platform = DM365;
 408        else if (!strcmp(platform_name, "dm644x_vpss"))
 409                oper_cfg.platform = DM644X;
 410        else {
 411                dev_err(&pdev->dev, "vpss driver not supported on this platform\n");
 412                return -ENODEV;
 413        }
 414
 415        dev_info(&pdev->dev, "%s vpss probed\n", platform_name);
 416        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 417
 418        oper_cfg.vpss_regs_base0 = devm_ioremap_resource(&pdev->dev, res);
 419        if (IS_ERR(oper_cfg.vpss_regs_base0))
 420                return PTR_ERR(oper_cfg.vpss_regs_base0);
 421
 422        if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) {
 423                res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 424
 425                oper_cfg.vpss_regs_base1 = devm_ioremap_resource(&pdev->dev,
 426                                                                 res);
 427                if (IS_ERR(oper_cfg.vpss_regs_base1))
 428                        return PTR_ERR(oper_cfg.vpss_regs_base1);
 429        }
 430
 431        if (oper_cfg.platform == DM355) {
 432                oper_cfg.hw_ops.enable_clock = dm355_enable_clock;
 433                oper_cfg.hw_ops.select_ccdc_source = dm355_select_ccdc_source;
 434                /* Setup vpss interrupts */
 435                bl_regw(DM355_VPSSBL_INTSEL_DEFAULT, DM355_VPSSBL_INTSEL);
 436                bl_regw(DM355_VPSSBL_EVTSEL_DEFAULT, DM355_VPSSBL_EVTSEL);
 437        } else if (oper_cfg.platform == DM365) {
 438                oper_cfg.hw_ops.enable_clock = dm365_enable_clock;
 439                oper_cfg.hw_ops.select_ccdc_source = dm365_select_ccdc_source;
 440                /* Setup vpss interrupts */
 441                isp5_write((isp5_read(DM365_ISP5_PCCR) |
 442                                      DM365_ISP5_PCCR_BL_CLK_ENABLE |
 443                                      DM365_ISP5_PCCR_ISIF_CLK_ENABLE |
 444                                      DM365_ISP5_PCCR_H3A_CLK_ENABLE |
 445                                      DM365_ISP5_PCCR_RSZ_CLK_ENABLE |
 446                                      DM365_ISP5_PCCR_IPIPE_CLK_ENABLE |
 447                                      DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE |
 448                                      DM365_ISP5_PCCR_RSV), DM365_ISP5_PCCR);
 449                isp5_write((isp5_read(DM365_ISP5_BCR) |
 450                            DM365_ISP5_BCR_ISIF_OUT_ENABLE), DM365_ISP5_BCR);
 451                isp5_write(DM365_ISP5_INTSEL1_DEFAULT, DM365_ISP5_INTSEL1);
 452                isp5_write(DM365_ISP5_INTSEL2_DEFAULT, DM365_ISP5_INTSEL2);
 453                isp5_write(DM365_ISP5_INTSEL3_DEFAULT, DM365_ISP5_INTSEL3);
 454        } else
 455                oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow;
 456
 457        pm_runtime_enable(&pdev->dev);
 458
 459        pm_runtime_get(&pdev->dev);
 460
 461        spin_lock_init(&oper_cfg.vpss_lock);
 462        dev_info(&pdev->dev, "%s vpss probe success\n", platform_name);
 463
 464        return 0;
 465}
 466
 467static int vpss_remove(struct platform_device *pdev)
 468{
 469        pm_runtime_disable(&pdev->dev);
 470        return 0;
 471}
 472
 473static int vpss_suspend(struct device *dev)
 474{
 475        pm_runtime_put(dev);
 476        return 0;
 477}
 478
 479static int vpss_resume(struct device *dev)
 480{
 481        pm_runtime_get(dev);
 482        return 0;
 483}
 484
 485static const struct dev_pm_ops vpss_pm_ops = {
 486        .suspend = vpss_suspend,
 487        .resume = vpss_resume,
 488};
 489
 490static struct platform_driver vpss_driver = {
 491        .driver = {
 492                .name   = "vpss",
 493                .pm = &vpss_pm_ops,
 494        },
 495        .remove = vpss_remove,
 496        .probe = vpss_probe,
 497};
 498
 499static void vpss_exit(void)
 500{
 501        platform_driver_unregister(&vpss_driver);
 502        iounmap(oper_cfg.vpss_regs_base2);
 503        release_mem_region(VPSS_CLK_CTRL, 4);
 504}
 505
 506static int __init vpss_init(void)
 507{
 508        int ret;
 509
 510        if (!request_mem_region(VPSS_CLK_CTRL, 4, "vpss_clock_control"))
 511                return -EBUSY;
 512
 513        oper_cfg.vpss_regs_base2 = ioremap(VPSS_CLK_CTRL, 4);
 514        if (unlikely(!oper_cfg.vpss_regs_base2)) {
 515                ret = -ENOMEM;
 516                goto err_ioremap;
 517        }
 518
 519        writel(VPSS_CLK_CTRL_VENCCLKEN |
 520               VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2);
 521
 522        ret = platform_driver_register(&vpss_driver);
 523        if (ret)
 524                goto err_pd_register;
 525
 526        return 0;
 527
 528err_pd_register:
 529        iounmap(oper_cfg.vpss_regs_base2);
 530err_ioremap:
 531        release_mem_region(VPSS_CLK_CTRL, 4);
 532        return ret;
 533}
 534subsys_initcall(vpss_init);
 535module_exit(vpss_exit);
 536