898 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * TI Camera Access Layer (CAL) - CAMERARX4 *5 * Copyright (c) 2015-2020 Texas Instruments Inc.6 *7 * Authors:8 * Benoit Parrot <bparrot@ti.com>9 * Laurent Pinchart <laurent.pinchart@ideasonboard.com>10 */11 12#include <linux/clk.h>13#include <linux/delay.h>14#include <linux/mfd/syscon.h>15#include <linux/module.h>16#include <linux/of_graph.h>17#include <linux/platform_device.h>18#include <linux/regmap.h>19#include <linux/slab.h>20 21#include <media/v4l2-ctrls.h>22#include <media/v4l2-fwnode.h>23#include <media/v4l2-subdev.h>24 25#include "cal.h"26#include "cal_regs.h"27 28/* ------------------------------------------------------------------29 * I/O Register Accessors30 * ------------------------------------------------------------------31 */32 33static inline u32 camerarx_read(struct cal_camerarx *phy, u32 offset)34{35 return ioread32(phy->base + offset);36}37 38static inline void camerarx_write(struct cal_camerarx *phy, u32 offset, u32 val)39{40 iowrite32(val, phy->base + offset);41}42 43/* ------------------------------------------------------------------44 * CAMERARX Management45 * ------------------------------------------------------------------46 */47 48static s64 cal_camerarx_get_ext_link_freq(struct cal_camerarx *phy)49{50 struct v4l2_mbus_config_mipi_csi2 *mipi_csi2 = &phy->endpoint.bus.mipi_csi2;51 u32 num_lanes = mipi_csi2->num_data_lanes;52 const struct cal_format_info *fmtinfo;53 struct v4l2_subdev_state *state;54 struct v4l2_mbus_framefmt *fmt;55 u32 bpp;56 s64 freq;57 58 state = v4l2_subdev_get_locked_active_state(&phy->subdev);59 60 fmt = v4l2_subdev_state_get_format(state, CAL_CAMERARX_PAD_SINK);61 62 fmtinfo = cal_format_by_code(fmt->code);63 if (!fmtinfo)64 return -EINVAL;65 66 bpp = fmtinfo->bpp;67 68 freq = v4l2_get_link_freq(phy->source->ctrl_handler, bpp, 2 * num_lanes);69 if (freq < 0) {70 phy_err(phy, "failed to get link freq for subdev '%s'\n",71 phy->source->name);72 return freq;73 }74 75 phy_dbg(3, phy, "Source Link Freq: %llu\n", freq);76 77 return freq;78}79 80static void cal_camerarx_lane_config(struct cal_camerarx *phy)81{82 u32 val = cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance));83 u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK;84 u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK;85 struct v4l2_mbus_config_mipi_csi2 *mipi_csi2 =86 &phy->endpoint.bus.mipi_csi2;87 int lane;88 89 cal_set_field(&val, mipi_csi2->clock_lane + 1, lane_mask);90 cal_set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask);91 for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) {92 /*93 * Every lane are one nibble apart starting with the94 * clock followed by the data lanes so shift masks by 4.95 */96 lane_mask <<= 4;97 polarity_mask <<= 4;98 cal_set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask);99 cal_set_field(&val, mipi_csi2->lane_polarities[lane + 1],100 polarity_mask);101 }102 103 cal_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val);104 phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n",105 phy->instance, val);106}107 108static void cal_camerarx_enable(struct cal_camerarx *phy)109{110 u32 num_lanes = phy->cal->data->camerarx[phy->instance].num_lanes;111 112 regmap_field_write(phy->fields[F_CAMMODE], 0);113 /* Always enable all lanes at the phy control level */114 regmap_field_write(phy->fields[F_LANEENABLE], (1 << num_lanes) - 1);115 /* F_CSI_MODE is not present on every architecture */116 if (phy->fields[F_CSI_MODE])117 regmap_field_write(phy->fields[F_CSI_MODE], 1);118 regmap_field_write(phy->fields[F_CTRLCLKEN], 1);119}120 121void cal_camerarx_disable(struct cal_camerarx *phy)122{123 regmap_field_write(phy->fields[F_CTRLCLKEN], 0);124}125 126/*127 * TCLK values are OK at their reset values128 */129#define TCLK_TERM 0130#define TCLK_MISS 1131#define TCLK_SETTLE 14132 133static void cal_camerarx_config(struct cal_camerarx *phy, s64 link_freq)134{135 unsigned int reg0, reg1;136 unsigned int ths_term, ths_settle;137 138 /* DPHY timing configuration */139 140 /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */141 ths_term = div_s64(20 * link_freq, 1000 * 1000 * 1000);142 phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term);143 144 /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */145 ths_settle = div_s64(105 * link_freq, 1000 * 1000 * 1000) + 4;146 phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle);147 148 reg0 = camerarx_read(phy, CAL_CSI2_PHY_REG0);149 cal_set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE,150 CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK);151 cal_set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK);152 cal_set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK);153 154 phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0);155 camerarx_write(phy, CAL_CSI2_PHY_REG0, reg0);156 157 reg1 = camerarx_read(phy, CAL_CSI2_PHY_REG1);158 cal_set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK);159 cal_set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK);160 cal_set_field(®1, TCLK_MISS,161 CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK);162 cal_set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK);163 164 phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1);165 camerarx_write(phy, CAL_CSI2_PHY_REG1, reg1);166}167 168static void cal_camerarx_power(struct cal_camerarx *phy, bool enable)169{170 u32 target_state;171 unsigned int i;172 173 target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON :174 CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF;175 176 cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance),177 target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK);178 179 for (i = 0; i < 10; i++) {180 u32 current_state;181 182 current_state = cal_read_field(phy->cal,183 CAL_CSI2_COMPLEXIO_CFG(phy->instance),184 CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK);185 186 if (current_state == target_state)187 break;188 189 usleep_range(1000, 1100);190 }191 192 if (i == 10)193 phy_err(phy, "Failed to power %s complexio\n",194 enable ? "up" : "down");195}196 197static void cal_camerarx_wait_reset(struct cal_camerarx *phy)198{199 unsigned long timeout;200 201 timeout = jiffies + msecs_to_jiffies(750);202 while (time_before(jiffies, timeout)) {203 if (cal_read_field(phy->cal,204 CAL_CSI2_COMPLEXIO_CFG(phy->instance),205 CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) ==206 CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED)207 break;208 usleep_range(500, 5000);209 }210 211 if (cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance),212 CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) !=213 CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED)214 phy_err(phy, "Timeout waiting for Complex IO reset done\n");215}216 217static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy)218{219 unsigned long timeout;220 221 timeout = jiffies + msecs_to_jiffies(750);222 while (time_before(jiffies, timeout)) {223 if (cal_read_field(phy->cal,224 CAL_CSI2_TIMING(phy->instance),225 CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0)226 break;227 usleep_range(500, 5000);228 }229 230 if (cal_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance),231 CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0)232 phy_err(phy, "Timeout waiting for stop state\n");233}234 235static void cal_camerarx_enable_irqs(struct cal_camerarx *phy)236{237 const u32 cio_err_mask =238 CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK |239 CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK |240 CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK |241 CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK;242 const u32 vc_err_mask =243 CAL_CSI2_VC_IRQ_CS_IRQ_MASK(0) |244 CAL_CSI2_VC_IRQ_CS_IRQ_MASK(1) |245 CAL_CSI2_VC_IRQ_CS_IRQ_MASK(2) |246 CAL_CSI2_VC_IRQ_CS_IRQ_MASK(3) |247 CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(0) |248 CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(1) |249 CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(2) |250 CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(3);251 252 /* Enable CIO & VC error IRQs. */253 cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0),254 CAL_HL_IRQ_CIO_MASK(phy->instance) |255 CAL_HL_IRQ_VC_MASK(phy->instance));256 cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance),257 cio_err_mask);258 cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(phy->instance),259 vc_err_mask);260}261 262static void cal_camerarx_disable_irqs(struct cal_camerarx *phy)263{264 /* Disable CIO error irqs */265 cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0),266 CAL_HL_IRQ_CIO_MASK(phy->instance) |267 CAL_HL_IRQ_VC_MASK(phy->instance));268 cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0);269 cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(phy->instance), 0);270}271 272static void cal_camerarx_ppi_enable(struct cal_camerarx *phy)273{274 cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance),275 1, CAL_CSI2_PPI_CTRL_ECC_EN_MASK);276 277 cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance),278 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK);279}280 281static void cal_camerarx_ppi_disable(struct cal_camerarx *phy)282{283 cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance),284 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK);285}286 287static int cal_camerarx_start(struct cal_camerarx *phy)288{289 s64 link_freq;290 u32 sscounter;291 u32 val;292 int ret;293 294 if (phy->enable_count > 0) {295 phy->enable_count++;296 return 0;297 }298 299 link_freq = cal_camerarx_get_ext_link_freq(phy);300 if (link_freq < 0)301 return link_freq;302 303 ret = v4l2_subdev_call(phy->source, core, s_power, 1);304 if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) {305 phy_err(phy, "power on failed in subdev\n");306 return ret;307 }308 309 cal_camerarx_enable_irqs(phy);310 311 /*312 * CSI-2 PHY Link Initialization Sequence, according to the DRA74xP /313 * DRA75xP / DRA76xP / DRA77xP TRM. The DRA71x / DRA72x and the AM65x /314 * DRA80xM TRMs have a slightly simplified sequence.315 */316 317 /*318 * 1. Configure all CSI-2 low level protocol registers to be ready to319 * receive signals/data from the CSI-2 PHY.320 *321 * i.-v. Configure the lanes position and polarity.322 */323 cal_camerarx_lane_config(phy);324 325 /*326 * vi.-vii. Configure D-PHY mode, enable the required lanes and327 * enable the CAMERARX clock.328 */329 cal_camerarx_enable(phy);330 331 /*332 * 2. CSI PHY and link initialization sequence.333 *334 * a. Deassert the CSI-2 PHY reset. Do not wait for reset completion335 * at this point, as it requires the external source to send the336 * CSI-2 HS clock.337 */338 cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance),339 CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL,340 CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK);341 phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n",342 phy->instance,343 cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)));344 345 /* Dummy read to allow SCP reset to complete. */346 camerarx_read(phy, CAL_CSI2_PHY_REG0);347 348 /* Program the PHY timing parameters. */349 cal_camerarx_config(phy, link_freq);350 351 /*352 * b. Assert the FORCERXMODE signal.353 *354 * The stop-state-counter is based on fclk cycles, and we always use355 * the x16 and x4 settings, so stop-state-timeout =356 * fclk-cycle * 16 * 4 * counter.357 *358 * Stop-state-timeout must be more than 100us as per CSI-2 spec, so we359 * calculate a timeout that's 100us (rounding up).360 */361 sscounter = DIV_ROUND_UP(clk_get_rate(phy->cal->fclk), 10000 * 16 * 4);362 363 val = cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance));364 cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK);365 cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK);366 cal_set_field(&val, sscounter,367 CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK);368 cal_write(phy->cal, CAL_CSI2_TIMING(phy->instance), val);369 phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n",370 phy->instance,371 cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance)));372 373 /* Assert the FORCERXMODE signal. */374 cal_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance),375 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK);376 phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n",377 phy->instance,378 cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance)));379 380 /*381 * c. Connect pull-down on CSI-2 PHY link (using pad control).382 *383 * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not384 * implemented.385 */386 387 /*388 * d. Power up the CSI-2 PHY.389 * e. Check whether the state status reaches the ON state.390 */391 cal_camerarx_power(phy, true);392 393 /*394 * Start the source to enable the CSI-2 HS clock. We can now wait for395 * CSI-2 PHY reset to complete.396 */397 ret = v4l2_subdev_call(phy->source, video, s_stream, 1);398 if (ret) {399 v4l2_subdev_call(phy->source, core, s_power, 0);400 cal_camerarx_disable_irqs(phy);401 phy_err(phy, "stream on failed in subdev\n");402 return ret;403 }404 405 cal_camerarx_wait_reset(phy);406 407 /* f. Wait for STOPSTATE=1 for all enabled lane modules. */408 cal_camerarx_wait_stop_state(phy);409 410 phy_dbg(1, phy, "CSI2_%u_REG1 = 0x%08x (bits 31-28 should be set)\n",411 phy->instance, camerarx_read(phy, CAL_CSI2_PHY_REG1));412 413 /*414 * g. Disable pull-down on CSI-2 PHY link (using pad control).415 *416 * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not417 * implemented.418 */419 420 /* Finally, enable the PHY Protocol Interface (PPI). */421 cal_camerarx_ppi_enable(phy);422 423 phy->enable_count++;424 425 return 0;426}427 428static void cal_camerarx_stop(struct cal_camerarx *phy)429{430 int ret;431 432 if (--phy->enable_count > 0)433 return;434 435 cal_camerarx_ppi_disable(phy);436 437 cal_camerarx_disable_irqs(phy);438 439 cal_camerarx_power(phy, false);440 441 /* Assert Complex IO Reset */442 cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance),443 CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL,444 CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK);445 446 phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset\n",447 phy->instance,448 cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)));449 450 /* Disable the phy */451 cal_camerarx_disable(phy);452 453 if (v4l2_subdev_call(phy->source, video, s_stream, 0))454 phy_err(phy, "stream off failed in subdev\n");455 456 ret = v4l2_subdev_call(phy->source, core, s_power, 0);457 if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)458 phy_err(phy, "power off failed in subdev\n");459}460 461/*462 * Errata i913: CSI2 LDO Needs to be disabled when module is powered on463 *464 * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2465 * LDOs on the device are disabled if CSI-2 module is powered on466 * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304467 * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high468 * current draw on the module supply in active mode.469 *470 * Errata does not apply when CSI-2 module is powered off471 * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0).472 *473 * SW Workaround:474 * Set the following register bits to disable the LDO,475 * which is essentially CSI2 REG10 bit 6:476 *477 * Core 0: 0x4845 B828 = 0x0000 0040478 * Core 1: 0x4845 B928 = 0x0000 0040479 */480void cal_camerarx_i913_errata(struct cal_camerarx *phy)481{482 u32 reg10 = camerarx_read(phy, CAL_CSI2_PHY_REG10);483 484 cal_set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK);485 486 phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10);487 camerarx_write(phy, CAL_CSI2_PHY_REG10, reg10);488}489 490static int cal_camerarx_regmap_init(struct cal_dev *cal,491 struct cal_camerarx *phy)492{493 const struct cal_camerarx_data *phy_data;494 unsigned int i;495 496 if (!cal->data)497 return -EINVAL;498 499 phy_data = &cal->data->camerarx[phy->instance];500 501 for (i = 0; i < F_MAX_FIELDS; i++) {502 struct reg_field field = {503 .reg = cal->syscon_camerrx_offset,504 .lsb = phy_data->fields[i].lsb,505 .msb = phy_data->fields[i].msb,506 };507 508 /*509 * Here we update the reg offset with the510 * value found in DT511 */512 phy->fields[i] = devm_regmap_field_alloc(cal->dev,513 cal->syscon_camerrx,514 field);515 if (IS_ERR(phy->fields[i])) {516 cal_err(cal, "Unable to allocate regmap fields\n");517 return PTR_ERR(phy->fields[i]);518 }519 }520 521 return 0;522}523 524static int cal_camerarx_parse_dt(struct cal_camerarx *phy)525{526 struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint;527 char data_lanes[V4L2_MBUS_CSI2_MAX_DATA_LANES * 2];528 struct device_node *ep_node;529 unsigned int i;530 int ret;531 532 /*533 * Find the endpoint node for the port corresponding to the PHY534 * instance, and parse its CSI-2-related properties.535 */536 ep_node = of_graph_get_endpoint_by_regs(phy->cal->dev->of_node,537 phy->instance, 0);538 if (!ep_node) {539 /*540 * The endpoint is not mandatory, not all PHY instances need to541 * be connected in DT.542 */543 phy_dbg(3, phy, "Port has no endpoint\n");544 return 0;545 }546 547 endpoint->bus_type = V4L2_MBUS_CSI2_DPHY;548 ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint);549 if (ret < 0) {550 phy_err(phy, "Failed to parse endpoint\n");551 goto done;552 }553 554 for (i = 0; i < endpoint->bus.mipi_csi2.num_data_lanes; i++) {555 unsigned int lane = endpoint->bus.mipi_csi2.data_lanes[i];556 557 if (lane > 4) {558 phy_err(phy, "Invalid position %u for data lane %u\n",559 lane, i);560 ret = -EINVAL;561 goto done;562 }563 564 data_lanes[i*2] = '0' + lane;565 data_lanes[i*2+1] = ' ';566 }567 568 data_lanes[i*2-1] = '\0';569 570 phy_dbg(3, phy,571 "CSI-2 bus: clock lane <%u>, data lanes <%s>, flags 0x%08x\n",572 endpoint->bus.mipi_csi2.clock_lane, data_lanes,573 endpoint->bus.mipi_csi2.flags);574 575 /* Retrieve the connected device and store it for later use. */576 phy->source_ep_node = of_graph_get_remote_endpoint(ep_node);577 phy->source_node = of_graph_get_port_parent(phy->source_ep_node);578 if (!phy->source_node) {579 phy_dbg(3, phy, "Can't get remote parent\n");580 of_node_put(phy->source_ep_node);581 ret = -EINVAL;582 goto done;583 }584 585 phy_dbg(1, phy, "Found connected device %pOFn\n", phy->source_node);586 587done:588 of_node_put(ep_node);589 return ret;590}591 592/* ------------------------------------------------------------------593 * V4L2 Subdev Operations594 * ------------------------------------------------------------------595 */596 597static inline struct cal_camerarx *to_cal_camerarx(struct v4l2_subdev *sd)598{599 return container_of(sd, struct cal_camerarx, subdev);600}601 602static int cal_camerarx_sd_s_stream(struct v4l2_subdev *sd, int enable)603{604 struct cal_camerarx *phy = to_cal_camerarx(sd);605 struct v4l2_subdev_state *state;606 int ret = 0;607 608 state = v4l2_subdev_lock_and_get_active_state(sd);609 610 if (enable)611 ret = cal_camerarx_start(phy);612 else613 cal_camerarx_stop(phy);614 615 v4l2_subdev_unlock_state(state);616 617 return ret;618}619 620static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd,621 struct v4l2_subdev_state *state,622 struct v4l2_subdev_mbus_code_enum *code)623{624 /* No transcoding, source and sink codes must match. */625 if (cal_rx_pad_is_source(code->pad)) {626 struct v4l2_mbus_framefmt *fmt;627 628 if (code->index > 0)629 return -EINVAL;630 631 fmt = v4l2_subdev_state_get_format(state,632 CAL_CAMERARX_PAD_SINK);633 code->code = fmt->code;634 } else {635 if (code->index >= cal_num_formats)636 return -EINVAL;637 638 code->code = cal_formats[code->index].code;639 }640 641 return 0;642}643 644static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd,645 struct v4l2_subdev_state *state,646 struct v4l2_subdev_frame_size_enum *fse)647{648 const struct cal_format_info *fmtinfo;649 650 if (fse->index > 0)651 return -EINVAL;652 653 /* No transcoding, source and sink formats must match. */654 if (cal_rx_pad_is_source(fse->pad)) {655 struct v4l2_mbus_framefmt *fmt;656 657 fmt = v4l2_subdev_state_get_format(state,658 CAL_CAMERARX_PAD_SINK);659 if (fse->code != fmt->code)660 return -EINVAL;661 662 fse->min_width = fmt->width;663 fse->max_width = fmt->width;664 fse->min_height = fmt->height;665 fse->max_height = fmt->height;666 } else {667 fmtinfo = cal_format_by_code(fse->code);668 if (!fmtinfo)669 return -EINVAL;670 671 fse->min_width = CAL_MIN_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8);672 fse->max_width = CAL_MAX_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8);673 fse->min_height = CAL_MIN_HEIGHT_LINES;674 fse->max_height = CAL_MAX_HEIGHT_LINES;675 }676 677 return 0;678}679 680static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd,681 struct v4l2_subdev_state *state,682 struct v4l2_subdev_format *format)683{684 const struct cal_format_info *fmtinfo;685 struct v4l2_mbus_framefmt *fmt;686 unsigned int bpp;687 688 /* No transcoding, source and sink formats must match. */689 if (cal_rx_pad_is_source(format->pad))690 return v4l2_subdev_get_fmt(sd, state, format);691 692 /*693 * Default to the first format if the requested media bus code isn't694 * supported.695 */696 fmtinfo = cal_format_by_code(format->format.code);697 if (!fmtinfo)698 fmtinfo = &cal_formats[0];699 700 /* Clamp the size, update the code. The colorspace is accepted as-is. */701 bpp = ALIGN(fmtinfo->bpp, 8);702 703 format->format.width = clamp_t(unsigned int, format->format.width,704 CAL_MIN_WIDTH_BYTES * 8 / bpp,705 CAL_MAX_WIDTH_BYTES * 8 / bpp);706 format->format.height = clamp_t(unsigned int, format->format.height,707 CAL_MIN_HEIGHT_LINES,708 CAL_MAX_HEIGHT_LINES);709 format->format.code = fmtinfo->code;710 format->format.field = V4L2_FIELD_NONE;711 712 /* Store the format and propagate it to the source pad. */713 714 fmt = v4l2_subdev_state_get_format(state, CAL_CAMERARX_PAD_SINK);715 *fmt = format->format;716 717 fmt = v4l2_subdev_state_get_format(state,718 CAL_CAMERARX_PAD_FIRST_SOURCE);719 *fmt = format->format;720 721 return 0;722}723 724static int cal_camerarx_sd_init_state(struct v4l2_subdev *sd,725 struct v4l2_subdev_state *state)726{727 struct v4l2_subdev_format format = {728 .which = state ? V4L2_SUBDEV_FORMAT_TRY729 : V4L2_SUBDEV_FORMAT_ACTIVE,730 .pad = CAL_CAMERARX_PAD_SINK,731 .format = {732 .width = 640,733 .height = 480,734 .code = MEDIA_BUS_FMT_UYVY8_1X16,735 .field = V4L2_FIELD_NONE,736 .colorspace = V4L2_COLORSPACE_SRGB,737 .ycbcr_enc = V4L2_YCBCR_ENC_601,738 .quantization = V4L2_QUANTIZATION_LIM_RANGE,739 .xfer_func = V4L2_XFER_FUNC_SRGB,740 },741 };742 743 return cal_camerarx_sd_set_fmt(sd, state, &format);744}745 746static int cal_camerarx_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,747 struct v4l2_mbus_frame_desc *fd)748{749 struct cal_camerarx *phy = to_cal_camerarx(sd);750 struct v4l2_mbus_frame_desc remote_desc;751 const struct media_pad *remote_pad;752 int ret;753 754 remote_pad = media_pad_remote_pad_first(&phy->pads[CAL_CAMERARX_PAD_SINK]);755 if (!remote_pad)756 return -EPIPE;757 758 ret = v4l2_subdev_call(phy->source, pad, get_frame_desc,759 remote_pad->index, &remote_desc);760 if (ret)761 return ret;762 763 if (remote_desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {764 cal_err(phy->cal,765 "Frame descriptor does not describe CSI-2 link");766 return -EINVAL;767 }768 769 if (remote_desc.num_entries > 1)770 cal_err(phy->cal,771 "Multiple streams not supported in remote frame descriptor, using the first one\n");772 773 fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;774 fd->num_entries = 1;775 fd->entry[0] = remote_desc.entry[0];776 777 return 0;778}779 780static const struct v4l2_subdev_video_ops cal_camerarx_video_ops = {781 .s_stream = cal_camerarx_sd_s_stream,782};783 784static const struct v4l2_subdev_pad_ops cal_camerarx_pad_ops = {785 .enum_mbus_code = cal_camerarx_sd_enum_mbus_code,786 .enum_frame_size = cal_camerarx_sd_enum_frame_size,787 .get_fmt = v4l2_subdev_get_fmt,788 .set_fmt = cal_camerarx_sd_set_fmt,789 .get_frame_desc = cal_camerarx_get_frame_desc,790};791 792static const struct v4l2_subdev_ops cal_camerarx_subdev_ops = {793 .video = &cal_camerarx_video_ops,794 .pad = &cal_camerarx_pad_ops,795};796 797static const struct v4l2_subdev_internal_ops cal_camerarx_internal_ops = {798 .init_state = cal_camerarx_sd_init_state,799};800 801static const struct media_entity_operations cal_camerarx_media_ops = {802 .link_validate = v4l2_subdev_link_validate,803};804 805/* ------------------------------------------------------------------806 * Create and Destroy807 * ------------------------------------------------------------------808 */809 810struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal,811 unsigned int instance)812{813 struct platform_device *pdev = to_platform_device(cal->dev);814 struct cal_camerarx *phy;815 struct v4l2_subdev *sd;816 unsigned int i;817 int ret;818 819 phy = devm_kzalloc(cal->dev, sizeof(*phy), GFP_KERNEL);820 if (!phy)821 return ERR_PTR(-ENOMEM);822 823 phy->cal = cal;824 phy->instance = instance;825 826 spin_lock_init(&phy->vc_lock);827 828 phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,829 (instance == 0) ?830 "cal_rx_core0" :831 "cal_rx_core1");832 phy->base = devm_ioremap_resource(cal->dev, phy->res);833 if (IS_ERR(phy->base)) {834 cal_err(cal, "failed to ioremap\n");835 return ERR_CAST(phy->base);836 }837 838 cal_dbg(1, cal, "ioresource %s at %pa - %pa\n",839 phy->res->name, &phy->res->start, &phy->res->end);840 841 ret = cal_camerarx_regmap_init(cal, phy);842 if (ret)843 return ERR_PTR(ret);844 845 ret = cal_camerarx_parse_dt(phy);846 if (ret)847 return ERR_PTR(ret);848 849 /* Initialize the V4L2 subdev and media entity. */850 sd = &phy->subdev;851 v4l2_subdev_init(sd, &cal_camerarx_subdev_ops);852 sd->internal_ops = &cal_camerarx_internal_ops;853 sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;854 sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;855 snprintf(sd->name, sizeof(sd->name), "CAMERARX%u", instance);856 sd->dev = cal->dev;857 858 phy->pads[CAL_CAMERARX_PAD_SINK].flags = MEDIA_PAD_FL_SINK;859 for (i = CAL_CAMERARX_PAD_FIRST_SOURCE; i < CAL_CAMERARX_NUM_PADS; ++i)860 phy->pads[i].flags = MEDIA_PAD_FL_SOURCE;861 sd->entity.ops = &cal_camerarx_media_ops;862 ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(phy->pads),863 phy->pads);864 if (ret)865 goto err_node_put;866 867 ret = v4l2_subdev_init_finalize(sd);868 if (ret)869 goto err_entity_cleanup;870 871 ret = v4l2_device_register_subdev(&cal->v4l2_dev, sd);872 if (ret)873 goto err_free_state;874 875 return phy;876 877err_free_state:878 v4l2_subdev_cleanup(sd);879err_entity_cleanup:880 media_entity_cleanup(&phy->subdev.entity);881err_node_put:882 of_node_put(phy->source_ep_node);883 of_node_put(phy->source_node);884 return ERR_PTR(ret);885}886 887void cal_camerarx_destroy(struct cal_camerarx *phy)888{889 if (!phy)890 return;891 892 v4l2_device_unregister_subdev(&phy->subdev);893 v4l2_subdev_cleanup(&phy->subdev);894 media_entity_cleanup(&phy->subdev.entity);895 of_node_put(phy->source_ep_node);896 of_node_put(phy->source_node);897}898