linux/drivers/gpu/ipu-v3/ipu-prg.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2016-2017 Lucas Stach, Pengutronix
   4 */
   5
   6#include <drm/drm_fourcc.h>
   7#include <linux/clk.h>
   8#include <linux/err.h>
   9#include <linux/iopoll.h>
  10#include <linux/mfd/syscon.h>
  11#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
  12#include <linux/module.h>
  13#include <linux/of.h>
  14#include <linux/platform_device.h>
  15#include <linux/pm_runtime.h>
  16#include <linux/regmap.h>
  17#include <video/imx-ipu-v3.h>
  18
  19#include "ipu-prv.h"
  20
  21#define IPU_PRG_CTL                             0x00
  22#define  IPU_PRG_CTL_BYPASS(i)                  (1 << (0 + i))
  23#define  IPU_PRG_CTL_SOFT_ARID_MASK             0x3
  24#define  IPU_PRG_CTL_SOFT_ARID_SHIFT(i)         (8 + i * 2)
  25#define  IPU_PRG_CTL_SOFT_ARID(i, v)            ((v & 0x3) << (8 + 2 * i))
  26#define  IPU_PRG_CTL_SO(i)                      (1 << (16 + i))
  27#define  IPU_PRG_CTL_VFLIP(i)                   (1 << (19 + i))
  28#define  IPU_PRG_CTL_BLOCK_MODE(i)              (1 << (22 + i))
  29#define  IPU_PRG_CTL_CNT_LOAD_EN(i)             (1 << (25 + i))
  30#define  IPU_PRG_CTL_SOFTRST                    (1 << 30)
  31#define  IPU_PRG_CTL_SHADOW_EN                  (1 << 31)
  32
  33#define IPU_PRG_STATUS                          0x04
  34#define  IPU_PRG_STATUS_BUFFER0_READY(i)        (1 << (0 + i * 2))
  35#define  IPU_PRG_STATUS_BUFFER1_READY(i)        (1 << (1 + i * 2))
  36
  37#define IPU_PRG_QOS                             0x08
  38#define  IPU_PRG_QOS_ARID_MASK                  0xf
  39#define  IPU_PRG_QOS_ARID_SHIFT(i)              (0 + i * 4)
  40
  41#define IPU_PRG_REG_UPDATE                      0x0c
  42#define  IPU_PRG_REG_UPDATE_REG_UPDATE          (1 << 0)
  43
  44#define IPU_PRG_STRIDE(i)                       (0x10 + i * 0x4)
  45#define  IPU_PRG_STRIDE_STRIDE_MASK             0x3fff
  46
  47#define IPU_PRG_CROP_LINE                       0x1c
  48
  49#define IPU_PRG_THD                             0x20
  50
  51#define IPU_PRG_BADDR(i)                        (0x24 + i * 0x4)
  52
  53#define IPU_PRG_OFFSET(i)                       (0x30 + i * 0x4)
  54
  55#define IPU_PRG_ILO(i)                          (0x3c + i * 0x4)
  56
  57#define IPU_PRG_HEIGHT(i)                       (0x48 + i * 0x4)
  58#define  IPU_PRG_HEIGHT_PRE_HEIGHT_MASK         0xfff
  59#define  IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT        0
  60#define  IPU_PRG_HEIGHT_IPU_HEIGHT_MASK         0xfff
  61#define  IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT        16
  62
  63struct ipu_prg_channel {
  64        bool                    enabled;
  65        int                     used_pre;
  66};
  67
  68struct ipu_prg {
  69        struct list_head        list;
  70        struct device           *dev;
  71        int                     id;
  72
  73        void __iomem            *regs;
  74        struct clk              *clk_ipg, *clk_axi;
  75        struct regmap           *iomuxc_gpr;
  76        struct ipu_pre          *pres[3];
  77
  78        struct ipu_prg_channel  chan[3];
  79};
  80
  81static DEFINE_MUTEX(ipu_prg_list_mutex);
  82static LIST_HEAD(ipu_prg_list);
  83
  84struct ipu_prg *
  85ipu_prg_lookup_by_phandle(struct device *dev, const char *name, int ipu_id)
  86{
  87        struct device_node *prg_node = of_parse_phandle(dev->of_node,
  88                                                        name, 0);
  89        struct ipu_prg *prg;
  90
  91        mutex_lock(&ipu_prg_list_mutex);
  92        list_for_each_entry(prg, &ipu_prg_list, list) {
  93                if (prg_node == prg->dev->of_node) {
  94                        mutex_unlock(&ipu_prg_list_mutex);
  95                        device_link_add(dev, prg->dev,
  96                                        DL_FLAG_AUTOREMOVE_CONSUMER);
  97                        prg->id = ipu_id;
  98                        of_node_put(prg_node);
  99                        return prg;
 100                }
 101        }
 102        mutex_unlock(&ipu_prg_list_mutex);
 103
 104        of_node_put(prg_node);
 105
 106        return NULL;
 107}
 108
 109int ipu_prg_max_active_channels(void)
 110{
 111        return ipu_pre_get_available_count();
 112}
 113EXPORT_SYMBOL_GPL(ipu_prg_max_active_channels);
 114
 115bool ipu_prg_present(struct ipu_soc *ipu)
 116{
 117        if (ipu->prg_priv)
 118                return true;
 119
 120        return false;
 121}
 122EXPORT_SYMBOL_GPL(ipu_prg_present);
 123
 124bool ipu_prg_format_supported(struct ipu_soc *ipu, uint32_t format,
 125                              uint64_t modifier)
 126{
 127        const struct drm_format_info *info = drm_format_info(format);
 128
 129        if (info->num_planes != 1)
 130                return false;
 131
 132        switch (modifier) {
 133        case DRM_FORMAT_MOD_LINEAR:
 134        case DRM_FORMAT_MOD_VIVANTE_TILED:
 135        case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED:
 136                return true;
 137        default:
 138                return false;
 139        }
 140}
 141EXPORT_SYMBOL_GPL(ipu_prg_format_supported);
 142
 143int ipu_prg_enable(struct ipu_soc *ipu)
 144{
 145        struct ipu_prg *prg = ipu->prg_priv;
 146
 147        if (!prg)
 148                return 0;
 149
 150        return pm_runtime_get_sync(prg->dev);
 151}
 152EXPORT_SYMBOL_GPL(ipu_prg_enable);
 153
 154void ipu_prg_disable(struct ipu_soc *ipu)
 155{
 156        struct ipu_prg *prg = ipu->prg_priv;
 157
 158        if (!prg)
 159                return;
 160
 161        pm_runtime_put(prg->dev);
 162}
 163EXPORT_SYMBOL_GPL(ipu_prg_disable);
 164
 165/*
 166 * The channel configuartion functions below are not thread safe, as they
 167 * must be only called from the atomic commit path in the DRM driver, which
 168 * is properly serialized.
 169 */
 170static int ipu_prg_ipu_to_prg_chan(int ipu_chan)
 171{
 172        /*
 173         * This isn't clearly documented in the RM, but IPU to PRG channel
 174         * assignment is fixed, as only with this mapping the control signals
 175         * match up.
 176         */
 177        switch (ipu_chan) {
 178        case IPUV3_CHANNEL_MEM_BG_SYNC:
 179                return 0;
 180        case IPUV3_CHANNEL_MEM_FG_SYNC:
 181                return 1;
 182        case IPUV3_CHANNEL_MEM_DC_SYNC:
 183                return 2;
 184        default:
 185                return -EINVAL;
 186        }
 187}
 188
 189static int ipu_prg_get_pre(struct ipu_prg *prg, int prg_chan)
 190{
 191        int i, ret;
 192
 193        /* channel 0 is special as it is hardwired to one of the PREs */
 194        if (prg_chan == 0) {
 195                ret = ipu_pre_get(prg->pres[0]);
 196                if (ret)
 197                        goto fail;
 198                prg->chan[prg_chan].used_pre = 0;
 199                return 0;
 200        }
 201
 202        for (i = 1; i < 3; i++) {
 203                ret = ipu_pre_get(prg->pres[i]);
 204                if (!ret) {
 205                        u32 val, mux;
 206                        int shift;
 207
 208                        prg->chan[prg_chan].used_pre = i;
 209
 210                        /* configure the PRE to PRG channel mux */
 211                        shift = (i == 1) ? 12 : 14;
 212                        mux = (prg->id << 1) | (prg_chan - 1);
 213                        regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5,
 214                                           0x3 << shift, mux << shift);
 215
 216                        /* check other mux, must not point to same channel */
 217                        shift = (i == 1) ? 14 : 12;
 218                        regmap_read(prg->iomuxc_gpr, IOMUXC_GPR5, &val);
 219                        if (((val >> shift) & 0x3) == mux) {
 220                                regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5,
 221                                                   0x3 << shift,
 222                                                   (mux ^ 0x1) << shift);
 223                        }
 224
 225                        return 0;
 226                }
 227        }
 228
 229fail:
 230        dev_err(prg->dev, "could not get PRE for PRG chan %d", prg_chan);
 231        return ret;
 232}
 233
 234static void ipu_prg_put_pre(struct ipu_prg *prg, int prg_chan)
 235{
 236        struct ipu_prg_channel *chan = &prg->chan[prg_chan];
 237
 238        ipu_pre_put(prg->pres[chan->used_pre]);
 239        chan->used_pre = -1;
 240}
 241
 242void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan)
 243{
 244        int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num);
 245        struct ipu_prg *prg = ipu_chan->ipu->prg_priv;
 246        struct ipu_prg_channel *chan;
 247        u32 val;
 248
 249        if (prg_chan < 0)
 250                return;
 251
 252        chan = &prg->chan[prg_chan];
 253        if (!chan->enabled)
 254                return;
 255
 256        pm_runtime_get_sync(prg->dev);
 257
 258        val = readl(prg->regs + IPU_PRG_CTL);
 259        val |= IPU_PRG_CTL_BYPASS(prg_chan);
 260        writel(val, prg->regs + IPU_PRG_CTL);
 261
 262        val = IPU_PRG_REG_UPDATE_REG_UPDATE;
 263        writel(val, prg->regs + IPU_PRG_REG_UPDATE);
 264
 265        pm_runtime_put(prg->dev);
 266
 267        ipu_prg_put_pre(prg, prg_chan);
 268
 269        chan->enabled = false;
 270}
 271EXPORT_SYMBOL_GPL(ipu_prg_channel_disable);
 272
 273int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan,
 274                              unsigned int axi_id, unsigned int width,
 275                              unsigned int height, unsigned int stride,
 276                              u32 format, uint64_t modifier, unsigned long *eba)
 277{
 278        int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num);
 279        struct ipu_prg *prg = ipu_chan->ipu->prg_priv;
 280        struct ipu_prg_channel *chan;
 281        u32 val;
 282        int ret;
 283
 284        if (prg_chan < 0)
 285                return prg_chan;
 286
 287        chan = &prg->chan[prg_chan];
 288
 289        if (chan->enabled) {
 290                ipu_pre_update(prg->pres[chan->used_pre], *eba);
 291                return 0;
 292        }
 293
 294        ret = ipu_prg_get_pre(prg, prg_chan);
 295        if (ret)
 296                return ret;
 297
 298        ipu_pre_configure(prg->pres[chan->used_pre],
 299                          width, height, stride, format, modifier, *eba);
 300
 301
 302        pm_runtime_get_sync(prg->dev);
 303
 304        val = (stride - 1) & IPU_PRG_STRIDE_STRIDE_MASK;
 305        writel(val, prg->regs + IPU_PRG_STRIDE(prg_chan));
 306
 307        val = ((height & IPU_PRG_HEIGHT_PRE_HEIGHT_MASK) <<
 308               IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT) |
 309              ((height & IPU_PRG_HEIGHT_IPU_HEIGHT_MASK) <<
 310               IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT);
 311        writel(val, prg->regs + IPU_PRG_HEIGHT(prg_chan));
 312
 313        val = ipu_pre_get_baddr(prg->pres[chan->used_pre]);
 314        *eba = val;
 315        writel(val, prg->regs + IPU_PRG_BADDR(prg_chan));
 316
 317        val = readl(prg->regs + IPU_PRG_CTL);
 318        /* config AXI ID */
 319        val &= ~(IPU_PRG_CTL_SOFT_ARID_MASK <<
 320                 IPU_PRG_CTL_SOFT_ARID_SHIFT(prg_chan));
 321        val |= IPU_PRG_CTL_SOFT_ARID(prg_chan, axi_id);
 322        /* enable channel */
 323        val &= ~IPU_PRG_CTL_BYPASS(prg_chan);
 324        writel(val, prg->regs + IPU_PRG_CTL);
 325
 326        val = IPU_PRG_REG_UPDATE_REG_UPDATE;
 327        writel(val, prg->regs + IPU_PRG_REG_UPDATE);
 328
 329        /* wait for both double buffers to be filled */
 330        readl_poll_timeout(prg->regs + IPU_PRG_STATUS, val,
 331                           (val & IPU_PRG_STATUS_BUFFER0_READY(prg_chan)) &&
 332                           (val & IPU_PRG_STATUS_BUFFER1_READY(prg_chan)),
 333                           5, 1000);
 334
 335        pm_runtime_put(prg->dev);
 336
 337        chan->enabled = true;
 338        return 0;
 339}
 340EXPORT_SYMBOL_GPL(ipu_prg_channel_configure);
 341
 342bool ipu_prg_channel_configure_pending(struct ipuv3_channel *ipu_chan)
 343{
 344        int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num);
 345        struct ipu_prg *prg = ipu_chan->ipu->prg_priv;
 346        struct ipu_prg_channel *chan;
 347
 348        if (prg_chan < 0)
 349                return false;
 350
 351        chan = &prg->chan[prg_chan];
 352        WARN_ON(!chan->enabled);
 353
 354        return ipu_pre_update_pending(prg->pres[chan->used_pre]);
 355}
 356EXPORT_SYMBOL_GPL(ipu_prg_channel_configure_pending);
 357
 358static int ipu_prg_probe(struct platform_device *pdev)
 359{
 360        struct device *dev = &pdev->dev;
 361        struct resource *res;
 362        struct ipu_prg *prg;
 363        u32 val;
 364        int i, ret;
 365
 366        prg = devm_kzalloc(dev, sizeof(*prg), GFP_KERNEL);
 367        if (!prg)
 368                return -ENOMEM;
 369
 370        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 371        prg->regs = devm_ioremap_resource(&pdev->dev, res);
 372        if (IS_ERR(prg->regs))
 373                return PTR_ERR(prg->regs);
 374
 375
 376        prg->clk_ipg = devm_clk_get(dev, "ipg");
 377        if (IS_ERR(prg->clk_ipg))
 378                return PTR_ERR(prg->clk_ipg);
 379
 380        prg->clk_axi = devm_clk_get(dev, "axi");
 381        if (IS_ERR(prg->clk_axi))
 382                return PTR_ERR(prg->clk_axi);
 383
 384        prg->iomuxc_gpr =
 385                syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
 386        if (IS_ERR(prg->iomuxc_gpr))
 387                return PTR_ERR(prg->iomuxc_gpr);
 388
 389        for (i = 0; i < 3; i++) {
 390                prg->pres[i] = ipu_pre_lookup_by_phandle(dev, "fsl,pres", i);
 391                if (!prg->pres[i])
 392                        return -EPROBE_DEFER;
 393        }
 394
 395        ret = clk_prepare_enable(prg->clk_ipg);
 396        if (ret)
 397                return ret;
 398
 399        ret = clk_prepare_enable(prg->clk_axi);
 400        if (ret) {
 401                clk_disable_unprepare(prg->clk_ipg);
 402                return ret;
 403        }
 404
 405        /* init to free running mode */
 406        val = readl(prg->regs + IPU_PRG_CTL);
 407        val |= IPU_PRG_CTL_SHADOW_EN;
 408        writel(val, prg->regs + IPU_PRG_CTL);
 409
 410        /* disable address threshold */
 411        writel(0xffffffff, prg->regs + IPU_PRG_THD);
 412
 413        pm_runtime_set_active(dev);
 414        pm_runtime_enable(dev);
 415
 416        prg->dev = dev;
 417        platform_set_drvdata(pdev, prg);
 418        mutex_lock(&ipu_prg_list_mutex);
 419        list_add(&prg->list, &ipu_prg_list);
 420        mutex_unlock(&ipu_prg_list_mutex);
 421
 422        return 0;
 423}
 424
 425static int ipu_prg_remove(struct platform_device *pdev)
 426{
 427        struct ipu_prg *prg = platform_get_drvdata(pdev);
 428
 429        mutex_lock(&ipu_prg_list_mutex);
 430        list_del(&prg->list);
 431        mutex_unlock(&ipu_prg_list_mutex);
 432
 433        return 0;
 434}
 435
 436#ifdef CONFIG_PM
 437static int prg_suspend(struct device *dev)
 438{
 439        struct ipu_prg *prg = dev_get_drvdata(dev);
 440
 441        clk_disable_unprepare(prg->clk_axi);
 442        clk_disable_unprepare(prg->clk_ipg);
 443
 444        return 0;
 445}
 446
 447static int prg_resume(struct device *dev)
 448{
 449        struct ipu_prg *prg = dev_get_drvdata(dev);
 450        int ret;
 451
 452        ret = clk_prepare_enable(prg->clk_ipg);
 453        if (ret)
 454                return ret;
 455
 456        ret = clk_prepare_enable(prg->clk_axi);
 457        if (ret) {
 458                clk_disable_unprepare(prg->clk_ipg);
 459                return ret;
 460        }
 461
 462        return 0;
 463}
 464#endif
 465
 466static const struct dev_pm_ops prg_pm_ops = {
 467        SET_RUNTIME_PM_OPS(prg_suspend, prg_resume, NULL)
 468};
 469
 470static const struct of_device_id ipu_prg_dt_ids[] = {
 471        { .compatible = "fsl,imx6qp-prg", },
 472        { /* sentinel */ },
 473};
 474
 475struct platform_driver ipu_prg_drv = {
 476        .probe          = ipu_prg_probe,
 477        .remove         = ipu_prg_remove,
 478        .driver         = {
 479                .name   = "imx-ipu-prg",
 480                .pm     = &prg_pm_ops,
 481                .of_match_table = ipu_prg_dt_ids,
 482        },
 483};
 484