linux/drivers/staging/media/atomisp/pci/atomisp_csi2.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Support for Medifield PNW Camera Imaging ISP subsystem.
   4 *
   5 * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public License version
   9 * 2 as published by the Free Software Foundation.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 *
  17 */
  18
  19#include <media/v4l2-event.h>
  20#include <media/v4l2-mediabus.h>
  21#include "atomisp_cmd.h"
  22#include "atomisp_internal.h"
  23#include "atomisp-regs.h"
  24
  25static struct v4l2_mbus_framefmt *__csi2_get_format(struct
  26        atomisp_mipi_csi2_device
  27        * csi2,
  28        struct v4l2_subdev_state *sd_state,
  29        enum
  30        v4l2_subdev_format_whence
  31        which, unsigned int pad)
  32{
  33        if (which == V4L2_SUBDEV_FORMAT_TRY)
  34                return v4l2_subdev_get_try_format(&csi2->subdev, sd_state,
  35                                                  pad);
  36        else
  37                return &csi2->formats[pad];
  38}
  39
  40/*
  41 * csi2_enum_mbus_code - Handle pixel format enumeration
  42 * @sd     : pointer to v4l2 subdev structure
  43 * @fh     : V4L2 subdev file handle
  44 * @code   : pointer to v4l2_subdev_pad_mbus_code_enum structure
  45 * return -EINVAL or zero on success
  46*/
  47static int csi2_enum_mbus_code(struct v4l2_subdev *sd,
  48                               struct v4l2_subdev_state *sd_state,
  49                               struct v4l2_subdev_mbus_code_enum *code)
  50{
  51        const struct atomisp_in_fmt_conv *ic = atomisp_in_fmt_conv;
  52        unsigned int i = 0;
  53
  54        while (ic->code) {
  55                if (i == code->index) {
  56                        code->code = ic->code;
  57                        return 0;
  58                }
  59                i++, ic++;
  60        }
  61
  62        return -EINVAL;
  63}
  64
  65/*
  66 * csi2_get_format - Handle get format by pads subdev method
  67 * @sd : pointer to v4l2 subdev structure
  68 * @fh : V4L2 subdev file handle
  69 * @pad: pad num
  70 * @fmt: pointer to v4l2 format structure
  71 * return -EINVAL or zero on success
  72*/
  73static int csi2_get_format(struct v4l2_subdev *sd,
  74                           struct v4l2_subdev_state *sd_state,
  75                           struct v4l2_subdev_format *fmt)
  76{
  77        struct atomisp_mipi_csi2_device *csi2 = v4l2_get_subdevdata(sd);
  78        struct v4l2_mbus_framefmt *format;
  79
  80        format = __csi2_get_format(csi2, sd_state, fmt->which, fmt->pad);
  81
  82        fmt->format = *format;
  83
  84        return 0;
  85}
  86
  87int atomisp_csi2_set_ffmt(struct v4l2_subdev *sd,
  88                          struct v4l2_subdev_state *sd_state,
  89                          unsigned int which, uint16_t pad,
  90                          struct v4l2_mbus_framefmt *ffmt)
  91{
  92        struct atomisp_mipi_csi2_device *csi2 = v4l2_get_subdevdata(sd);
  93        struct v4l2_mbus_framefmt *actual_ffmt = __csi2_get_format(csi2,
  94                                                                   sd_state,
  95                                                                   which, pad);
  96
  97        if (pad == CSI2_PAD_SINK) {
  98                const struct atomisp_in_fmt_conv *ic;
  99                struct v4l2_mbus_framefmt tmp_ffmt;
 100
 101                ic = atomisp_find_in_fmt_conv(ffmt->code);
 102                if (ic)
 103                        actual_ffmt->code = ic->code;
 104                else
 105                        actual_ffmt->code = atomisp_in_fmt_conv[0].code;
 106
 107                actual_ffmt->width = clamp_t(
 108                                         u32, ffmt->width, ATOM_ISP_MIN_WIDTH,
 109                                         ATOM_ISP_MAX_WIDTH);
 110                actual_ffmt->height = clamp_t(
 111                                          u32, ffmt->height, ATOM_ISP_MIN_HEIGHT,
 112                                          ATOM_ISP_MAX_HEIGHT);
 113
 114                tmp_ffmt = *ffmt = *actual_ffmt;
 115
 116                return atomisp_csi2_set_ffmt(sd, sd_state, which,
 117                                             CSI2_PAD_SOURCE,
 118                                             &tmp_ffmt);
 119        }
 120
 121        /* FIXME: DPCM decompression */
 122        *actual_ffmt = *ffmt = *__csi2_get_format(csi2, sd_state, which,
 123                                                  CSI2_PAD_SINK);
 124
 125        return 0;
 126}
 127
 128/*
 129 * csi2_set_format - Handle set format by pads subdev method
 130 * @sd : pointer to v4l2 subdev structure
 131 * @fh : V4L2 subdev file handle
 132 * @pad: pad num
 133 * @fmt: pointer to v4l2 format structure
 134 * return -EINVAL or zero on success
 135*/
 136static int csi2_set_format(struct v4l2_subdev *sd,
 137                           struct v4l2_subdev_state *sd_state,
 138                           struct v4l2_subdev_format *fmt)
 139{
 140        return atomisp_csi2_set_ffmt(sd, sd_state, fmt->which, fmt->pad,
 141                                     &fmt->format);
 142}
 143
 144/*
 145 * csi2_set_stream - Enable/Disable streaming on the CSI2 module
 146 * @sd: ISP CSI2 V4L2 subdevice
 147 * @enable: Enable/disable stream (1/0)
 148 *
 149 * Return 0 on success or a negative error code otherwise.
 150*/
 151static int csi2_set_stream(struct v4l2_subdev *sd, int enable)
 152{
 153        return 0;
 154}
 155
 156/* subdev core operations */
 157static const struct v4l2_subdev_core_ops csi2_core_ops = {
 158};
 159
 160/* subdev video operations */
 161static const struct v4l2_subdev_video_ops csi2_video_ops = {
 162        .s_stream = csi2_set_stream,
 163};
 164
 165/* subdev pad operations */
 166static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
 167        .enum_mbus_code = csi2_enum_mbus_code,
 168        .get_fmt = csi2_get_format,
 169        .set_fmt = csi2_set_format,
 170        .link_validate = v4l2_subdev_link_validate_default,
 171};
 172
 173/* subdev operations */
 174static const struct v4l2_subdev_ops csi2_ops = {
 175        .core = &csi2_core_ops,
 176        .video = &csi2_video_ops,
 177        .pad = &csi2_pad_ops,
 178};
 179
 180/*
 181 * csi2_link_setup - Setup CSI2 connections.
 182 * @entity : Pointer to media entity structure
 183 * @local  : Pointer to local pad array
 184 * @remote : Pointer to remote pad array
 185 * @flags  : Link flags
 186 * return -EINVAL or zero on success
 187*/
 188static int csi2_link_setup(struct media_entity *entity,
 189                           const struct media_pad *local,
 190                           const struct media_pad *remote, u32 flags)
 191{
 192        struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
 193        struct atomisp_mipi_csi2_device *csi2 = v4l2_get_subdevdata(sd);
 194        u32 result = local->index | is_media_entity_v4l2_subdev(remote->entity);
 195
 196        switch (result) {
 197        case CSI2_PAD_SOURCE | MEDIA_ENT_F_OLD_BASE:
 198                /* not supported yet */
 199                return -EINVAL;
 200
 201        case CSI2_PAD_SOURCE | MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN:
 202                if (flags & MEDIA_LNK_FL_ENABLED) {
 203                        if (csi2->output & ~CSI2_OUTPUT_ISP_SUBDEV)
 204                                return -EBUSY;
 205                        csi2->output |= CSI2_OUTPUT_ISP_SUBDEV;
 206                } else {
 207                        csi2->output &= ~CSI2_OUTPUT_ISP_SUBDEV;
 208                }
 209                break;
 210
 211        default:
 212                /* Link from camera to CSI2 is fixed... */
 213                return -EINVAL;
 214        }
 215        return 0;
 216}
 217
 218/* media operations */
 219static const struct media_entity_operations csi2_media_ops = {
 220        .link_setup = csi2_link_setup,
 221        .link_validate = v4l2_subdev_link_validate,
 222};
 223
 224/*
 225* ispcsi2_init_entities - Initialize subdev and media entity.
 226* @csi2: Pointer to ispcsi2 structure.
 227* return -ENOMEM or zero on success
 228*/
 229static int mipi_csi2_init_entities(struct atomisp_mipi_csi2_device *csi2,
 230                                   int port)
 231{
 232        struct v4l2_subdev *sd = &csi2->subdev;
 233        struct media_pad *pads = csi2->pads;
 234        struct media_entity *me = &sd->entity;
 235        int ret;
 236
 237        v4l2_subdev_init(sd, &csi2_ops);
 238        snprintf(sd->name, sizeof(sd->name), "ATOM ISP CSI2-port%d", port);
 239
 240        v4l2_set_subdevdata(sd, csi2);
 241        sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
 242
 243        pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
 244        pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
 245
 246        me->ops = &csi2_media_ops;
 247        me->function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
 248        ret = media_entity_pads_init(me, CSI2_PADS_NUM, pads);
 249        if (ret < 0)
 250                return ret;
 251
 252        csi2->formats[CSI2_PAD_SINK].code =
 253            csi2->formats[CSI2_PAD_SOURCE].code =
 254                atomisp_in_fmt_conv[0].code;
 255
 256        return 0;
 257}
 258
 259void
 260atomisp_mipi_csi2_unregister_entities(struct atomisp_mipi_csi2_device *csi2)
 261{
 262        media_entity_cleanup(&csi2->subdev.entity);
 263        v4l2_device_unregister_subdev(&csi2->subdev);
 264}
 265
 266int atomisp_mipi_csi2_register_entities(struct atomisp_mipi_csi2_device *csi2,
 267                                        struct v4l2_device *vdev)
 268{
 269        int ret;
 270
 271        /* Register the subdev and video nodes. */
 272        ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
 273        if (ret < 0)
 274                goto error;
 275
 276        return 0;
 277
 278error:
 279        atomisp_mipi_csi2_unregister_entities(csi2);
 280        return ret;
 281}
 282
 283static const int LIMIT_SHIFT = 6;       /* Limit numeric range into 31 bits */
 284
 285static int
 286atomisp_csi2_configure_calc(const short int coeffs[2], int mipi_freq, int def)
 287{
 288        /* Delay counter accuracy, 1/0.0625 for ANN/CHT, 1/0.125 for BXT */
 289        static const int accinv = 16;           /* 1 / COUNT_ACC */
 290        int r;
 291
 292        if (mipi_freq >> LIMIT_SHIFT <= 0)
 293                return def;
 294
 295        r = accinv * coeffs[1] * (500000000 >> LIMIT_SHIFT);
 296        r /= mipi_freq >> LIMIT_SHIFT;
 297        r += accinv * coeffs[0];
 298
 299        return r;
 300}
 301
 302static void atomisp_csi2_configure_isp2401(struct atomisp_sub_device *asd)
 303{
 304        /*
 305         * The ISP2401 new input system CSI2+ receiver has several
 306         * parameters affecting the receiver timings. These depend
 307         * on the MIPI bus frequency F in Hz (sensor transmitter rate)
 308         * as follows:
 309         *      register value = (A/1e9 + B * UI) / COUNT_ACC
 310         * where
 311         *      UI = 1 / (2 * F) in seconds
 312         *      COUNT_ACC = counter accuracy in seconds
 313         *      For ANN and CHV, COUNT_ACC = 0.0625 ns
 314         *      For BXT,  COUNT_ACC = 0.125 ns
 315         * A and B are coefficients from the table below,
 316         * depending whether the register minimum or maximum value is
 317         * calculated.
 318         *                                     Minimum     Maximum
 319         * Clock lane                          A     B     A     B
 320         * reg_rx_csi_dly_cnt_termen_clane     0     0    38     0
 321         * reg_rx_csi_dly_cnt_settle_clane    95    -8   300   -16
 322         * Data lanes
 323         * reg_rx_csi_dly_cnt_termen_dlane0    0     0    35     4
 324         * reg_rx_csi_dly_cnt_settle_dlane0   85    -2   145    -6
 325         * reg_rx_csi_dly_cnt_termen_dlane1    0     0    35     4
 326         * reg_rx_csi_dly_cnt_settle_dlane1   85    -2   145    -6
 327         * reg_rx_csi_dly_cnt_termen_dlane2    0     0    35     4
 328         * reg_rx_csi_dly_cnt_settle_dlane2   85    -2   145    -6
 329         * reg_rx_csi_dly_cnt_termen_dlane3    0     0    35     4
 330         * reg_rx_csi_dly_cnt_settle_dlane3   85    -2   145    -6
 331         *
 332         * We use the minimum values in the calculations below.
 333         */
 334        static const short int coeff_clk_termen[] = { 0, 0 };
 335        static const short int coeff_clk_settle[] = { 95, -8 };
 336        static const short int coeff_dat_termen[] = { 0, 0 };
 337        static const short int coeff_dat_settle[] = { 85, -2 };
 338        static const int TERMEN_DEFAULT           = 0 * 0;
 339        static const int SETTLE_DEFAULT           = 0x480;
 340
 341        static const hrt_address csi2_port_base[] = {
 342                [ATOMISP_CAMERA_PORT_PRIMARY]     = CSI2_PORT_A_BASE,
 343                [ATOMISP_CAMERA_PORT_SECONDARY]   = CSI2_PORT_B_BASE,
 344                [ATOMISP_CAMERA_PORT_TERTIARY]    = CSI2_PORT_C_BASE,
 345        };
 346        /* Number of lanes on each port, excluding clock lane */
 347        static const unsigned char csi2_port_lanes[] = {
 348                [ATOMISP_CAMERA_PORT_PRIMARY]     = 4,
 349                [ATOMISP_CAMERA_PORT_SECONDARY]   = 2,
 350                [ATOMISP_CAMERA_PORT_TERTIARY]    = 2,
 351        };
 352        static const hrt_address csi2_lane_base[] = {
 353                CSI2_LANE_CL_BASE,
 354                CSI2_LANE_D0_BASE,
 355                CSI2_LANE_D1_BASE,
 356                CSI2_LANE_D2_BASE,
 357                CSI2_LANE_D3_BASE,
 358        };
 359
 360        int clk_termen;
 361        int clk_settle;
 362        int dat_termen;
 363        int dat_settle;
 364
 365        struct v4l2_control ctrl;
 366        struct atomisp_device *isp = asd->isp;
 367        struct camera_mipi_info *mipi_info;
 368        int mipi_freq = 0;
 369        enum atomisp_camera_port port;
 370
 371        int n;
 372
 373        mipi_info = atomisp_to_sensor_mipi_info(
 374                        isp->inputs[asd->input_curr].camera);
 375        port = mipi_info->port;
 376
 377        ctrl.id = V4L2_CID_LINK_FREQ;
 378        if (v4l2_g_ctrl
 379            (isp->inputs[asd->input_curr].camera->ctrl_handler, &ctrl) == 0)
 380                mipi_freq = ctrl.value;
 381
 382        clk_termen = atomisp_csi2_configure_calc(coeff_clk_termen,
 383                     mipi_freq, TERMEN_DEFAULT);
 384        clk_settle = atomisp_csi2_configure_calc(coeff_clk_settle,
 385                     mipi_freq, SETTLE_DEFAULT);
 386        dat_termen = atomisp_csi2_configure_calc(coeff_dat_termen,
 387                     mipi_freq, TERMEN_DEFAULT);
 388        dat_settle = atomisp_csi2_configure_calc(coeff_dat_settle,
 389                     mipi_freq, SETTLE_DEFAULT);
 390        for (n = 0; n < csi2_port_lanes[port] + 1; n++) {
 391                hrt_address base = csi2_port_base[port] + csi2_lane_base[n];
 392
 393                atomisp_css2_hw_store_32(base + CSI2_REG_RX_CSI_DLY_CNT_TERMEN,
 394                                     n == 0 ? clk_termen : dat_termen);
 395                atomisp_css2_hw_store_32(base + CSI2_REG_RX_CSI_DLY_CNT_SETTLE,
 396                                     n == 0 ? clk_settle : dat_settle);
 397        }
 398}
 399
 400void atomisp_csi2_configure(struct atomisp_sub_device *asd)
 401{
 402        if (IS_HWREVISION(asd->isp, ATOMISP_HW_REVISION_ISP2401))
 403                atomisp_csi2_configure_isp2401(asd);
 404}
 405
 406/*
 407 * atomisp_mipi_csi2_cleanup - Routine for module driver cleanup
 408*/
 409void atomisp_mipi_csi2_cleanup(struct atomisp_device *isp)
 410{
 411}
 412
 413int atomisp_mipi_csi2_init(struct atomisp_device *isp)
 414{
 415        struct atomisp_mipi_csi2_device *csi2_port;
 416        unsigned int i;
 417        int ret;
 418
 419        for (i = 0; i < ATOMISP_CAMERA_NR_PORTS; i++) {
 420                csi2_port = &isp->csi2_port[i];
 421                csi2_port->isp = isp;
 422                ret = mipi_csi2_init_entities(csi2_port, i);
 423                if (ret < 0)
 424                        goto fail;
 425        }
 426
 427        return 0;
 428
 429fail:
 430        atomisp_mipi_csi2_cleanup(isp);
 431        return ret;
 432}
 433