linux/drivers/staging/media/omap4iss/iss_csiphy.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * TI OMAP4 ISS V4L2 Driver - CSI PHY module
   4 *
   5 * Copyright (C) 2012 Texas Instruments, Inc.
   6 *
   7 * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com>
   8 */
   9
  10#include <linux/delay.h>
  11#include <linux/device.h>
  12#include <linux/regmap.h>
  13
  14#include "../../../../arch/arm/mach-omap2/control.h"
  15
  16#include "iss.h"
  17#include "iss_regs.h"
  18#include "iss_csiphy.h"
  19
  20/*
  21 * csiphy_lanes_config - Configuration of CSIPHY lanes.
  22 *
  23 * Updates HW configuration.
  24 * Called with phy->mutex taken.
  25 */
  26static void csiphy_lanes_config(struct iss_csiphy *phy)
  27{
  28        unsigned int i;
  29        u32 reg;
  30
  31        reg = iss_reg_read(phy->iss, phy->cfg_regs, CSI2_COMPLEXIO_CFG);
  32
  33        for (i = 0; i < phy->max_data_lanes; i++) {
  34                reg &= ~(CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) |
  35                         CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i + 1));
  36                reg |= (phy->lanes.data[i].pol ?
  37                        CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) : 0);
  38                reg |= (phy->lanes.data[i].pos <<
  39                        CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i + 1));
  40        }
  41
  42        reg &= ~(CSI2_COMPLEXIO_CFG_CLOCK_POL |
  43                 CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK);
  44        reg |= phy->lanes.clk.pol ? CSI2_COMPLEXIO_CFG_CLOCK_POL : 0;
  45        reg |= phy->lanes.clk.pos << CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT;
  46
  47        iss_reg_write(phy->iss, phy->cfg_regs, CSI2_COMPLEXIO_CFG, reg);
  48}
  49
  50/*
  51 * csiphy_set_power
  52 * @power: Power state to be set.
  53 *
  54 * Returns 0 if successful, or -EBUSY if the retry count is exceeded.
  55 */
  56static int csiphy_set_power(struct iss_csiphy *phy, u32 power)
  57{
  58        u32 reg;
  59        u8 retry_count;
  60
  61        iss_reg_update(phy->iss, phy->cfg_regs, CSI2_COMPLEXIO_CFG,
  62                       CSI2_COMPLEXIO_CFG_PWD_CMD_MASK,
  63                       power | CSI2_COMPLEXIO_CFG_PWR_AUTO);
  64
  65        retry_count = 0;
  66        do {
  67                udelay(1);
  68                reg = iss_reg_read(phy->iss, phy->cfg_regs, CSI2_COMPLEXIO_CFG)
  69                    & CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK;
  70
  71                if (reg != power >> 2)
  72                        retry_count++;
  73
  74        } while ((reg != power >> 2) && (retry_count < 250));
  75
  76        if (retry_count == 250) {
  77                dev_err(phy->iss->dev, "CSI2 CIO set power failed!\n");
  78                return -EBUSY;
  79        }
  80
  81        return 0;
  82}
  83
  84/*
  85 * csiphy_dphy_config - Configure CSI2 D-PHY parameters.
  86 *
  87 * Called with phy->mutex taken.
  88 */
  89static void csiphy_dphy_config(struct iss_csiphy *phy)
  90{
  91        u32 reg;
  92
  93        /* Set up REGISTER0 */
  94        reg = phy->dphy.ths_term << REGISTER0_THS_TERM_SHIFT;
  95        reg |= phy->dphy.ths_settle << REGISTER0_THS_SETTLE_SHIFT;
  96
  97        iss_reg_write(phy->iss, phy->phy_regs, REGISTER0, reg);
  98
  99        /* Set up REGISTER1 */
 100        reg = phy->dphy.tclk_term << REGISTER1_TCLK_TERM_SHIFT;
 101        reg |= phy->dphy.tclk_miss << REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT;
 102        reg |= phy->dphy.tclk_settle << REGISTER1_TCLK_SETTLE_SHIFT;
 103        reg |= 0xb8 << REGISTER1_DPHY_HS_SYNC_PATTERN_SHIFT;
 104
 105        iss_reg_write(phy->iss, phy->phy_regs, REGISTER1, reg);
 106}
 107
 108/*
 109 * TCLK values are OK at their reset values
 110 */
 111#define TCLK_TERM       0
 112#define TCLK_MISS       1
 113#define TCLK_SETTLE     14
 114
 115int omap4iss_csiphy_config(struct iss_device *iss,
 116                           struct v4l2_subdev *csi2_subdev)
 117{
 118        struct iss_csi2_device *csi2 = v4l2_get_subdevdata(csi2_subdev);
 119        struct iss_pipeline *pipe = to_iss_pipeline(&csi2_subdev->entity);
 120        struct iss_v4l2_subdevs_group *subdevs = pipe->external->host_priv;
 121        struct iss_csiphy_dphy_cfg csi2phy;
 122        int csi2_ddrclk_khz;
 123        struct iss_csiphy_lanes_cfg *lanes;
 124        unsigned int used_lanes = 0;
 125        u32 cam_rx_ctrl;
 126        unsigned int i;
 127
 128        lanes = &subdevs->bus.csi2.lanecfg;
 129
 130        /*
 131         * SCM.CONTROL_CAMERA_RX
 132         * - bit [31] : CSIPHY2 lane 2 enable (4460+ only)
 133         * - bit [30:29] : CSIPHY2 per-lane enable (1 to 0)
 134         * - bit [28:24] : CSIPHY1 per-lane enable (4 to 0)
 135         * - bit [21] : CSIPHY2 CTRLCLK enable
 136         * - bit [20:19] : CSIPHY2 config: 00 d-phy, 01/10 ccp2
 137         * - bit [18] : CSIPHY1 CTRLCLK enable
 138         * - bit [17:16] : CSIPHY1 config: 00 d-phy, 01/10 ccp2
 139         */
 140        /*
 141         * TODO: When implementing DT support specify the CONTROL_CAMERA_RX
 142         * register offset in the syscon property instead of hardcoding it.
 143         */
 144        regmap_read(iss->syscon, 0x68, &cam_rx_ctrl);
 145
 146        if (subdevs->interface == ISS_INTERFACE_CSI2A_PHY1) {
 147                cam_rx_ctrl &= ~(OMAP4_CAMERARX_CSI21_LANEENABLE_MASK |
 148                                OMAP4_CAMERARX_CSI21_CAMMODE_MASK);
 149                /* NOTE: Leave CSIPHY1 config to 0x0: D-PHY mode */
 150                /* Enable all lanes for now */
 151                cam_rx_ctrl |=
 152                        0x1f << OMAP4_CAMERARX_CSI21_LANEENABLE_SHIFT;
 153                /* Enable CTRLCLK */
 154                cam_rx_ctrl |= OMAP4_CAMERARX_CSI21_CTRLCLKEN_MASK;
 155        }
 156
 157        if (subdevs->interface == ISS_INTERFACE_CSI2B_PHY2) {
 158                cam_rx_ctrl &= ~(OMAP4_CAMERARX_CSI22_LANEENABLE_MASK |
 159                                OMAP4_CAMERARX_CSI22_CAMMODE_MASK);
 160                /* NOTE: Leave CSIPHY2 config to 0x0: D-PHY mode */
 161                /* Enable all lanes for now */
 162                cam_rx_ctrl |=
 163                        0x3 << OMAP4_CAMERARX_CSI22_LANEENABLE_SHIFT;
 164                /* Enable CTRLCLK */
 165                cam_rx_ctrl |= OMAP4_CAMERARX_CSI22_CTRLCLKEN_MASK;
 166        }
 167
 168        regmap_write(iss->syscon, 0x68, cam_rx_ctrl);
 169
 170        /* Reset used lane count */
 171        csi2->phy->used_data_lanes = 0;
 172
 173        /* Clock and data lanes verification */
 174        for (i = 0; i < csi2->phy->max_data_lanes; i++) {
 175                if (lanes->data[i].pos == 0)
 176                        continue;
 177
 178                if (lanes->data[i].pol > 1 ||
 179                    lanes->data[i].pos > (csi2->phy->max_data_lanes + 1))
 180                        return -EINVAL;
 181
 182                if (used_lanes & (1 << lanes->data[i].pos))
 183                        return -EINVAL;
 184
 185                used_lanes |= 1 << lanes->data[i].pos;
 186                csi2->phy->used_data_lanes++;
 187        }
 188
 189        if (lanes->clk.pol > 1 ||
 190            lanes->clk.pos > (csi2->phy->max_data_lanes + 1))
 191                return -EINVAL;
 192
 193        if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos))
 194                return -EINVAL;
 195
 196        csi2_ddrclk_khz = pipe->external_rate / 1000
 197                / (2 * csi2->phy->used_data_lanes)
 198                * pipe->external_bpp;
 199
 200        /*
 201         * THS_TERM: Programmed value = ceil(12.5 ns/DDRClk period) - 1.
 202         * THS_SETTLE: Programmed value = ceil(90 ns/DDRClk period) + 3.
 203         */
 204        csi2phy.ths_term = DIV_ROUND_UP(25 * csi2_ddrclk_khz, 2000000) - 1;
 205        csi2phy.ths_settle = DIV_ROUND_UP(90 * csi2_ddrclk_khz, 1000000) + 3;
 206        csi2phy.tclk_term = TCLK_TERM;
 207        csi2phy.tclk_miss = TCLK_MISS;
 208        csi2phy.tclk_settle = TCLK_SETTLE;
 209
 210        mutex_lock(&csi2->phy->mutex);
 211        csi2->phy->dphy = csi2phy;
 212        csi2->phy->lanes = *lanes;
 213        mutex_unlock(&csi2->phy->mutex);
 214
 215        return 0;
 216}
 217
 218int omap4iss_csiphy_acquire(struct iss_csiphy *phy)
 219{
 220        int rval;
 221
 222        mutex_lock(&phy->mutex);
 223
 224        rval = omap4iss_csi2_reset(phy->csi2);
 225        if (rval)
 226                goto done;
 227
 228        csiphy_dphy_config(phy);
 229        csiphy_lanes_config(phy);
 230
 231        rval = csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_ON);
 232        if (rval)
 233                goto done;
 234
 235        phy->phy_in_use = 1;
 236
 237done:
 238        mutex_unlock(&phy->mutex);
 239        return rval;
 240}
 241
 242void omap4iss_csiphy_release(struct iss_csiphy *phy)
 243{
 244        mutex_lock(&phy->mutex);
 245        if (phy->phy_in_use) {
 246                csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_OFF);
 247                phy->phy_in_use = 0;
 248        }
 249        mutex_unlock(&phy->mutex);
 250}
 251
 252/*
 253 * omap4iss_csiphy_init - Initialize the CSI PHY frontends
 254 */
 255int omap4iss_csiphy_init(struct iss_device *iss)
 256{
 257        struct iss_csiphy *phy1 = &iss->csiphy1;
 258        struct iss_csiphy *phy2 = &iss->csiphy2;
 259
 260        phy1->iss = iss;
 261        phy1->csi2 = &iss->csi2a;
 262        phy1->max_data_lanes = ISS_CSIPHY1_NUM_DATA_LANES;
 263        phy1->used_data_lanes = 0;
 264        phy1->cfg_regs = OMAP4_ISS_MEM_CSI2_A_REGS1;
 265        phy1->phy_regs = OMAP4_ISS_MEM_CAMERARX_CORE1;
 266        mutex_init(&phy1->mutex);
 267
 268        phy2->iss = iss;
 269        phy2->csi2 = &iss->csi2b;
 270        phy2->max_data_lanes = ISS_CSIPHY2_NUM_DATA_LANES;
 271        phy2->used_data_lanes = 0;
 272        phy2->cfg_regs = OMAP4_ISS_MEM_CSI2_B_REGS1;
 273        phy2->phy_regs = OMAP4_ISS_MEM_CAMERARX_CORE2;
 274        mutex_init(&phy2->mutex);
 275
 276        return 0;
 277}
 278