1e6938cc1SHelen Koike // SPDX-License-Identifier: (GPL-2.0+ OR MIT) 2e6938cc1SHelen Koike /* 3e6938cc1SHelen Koike * Rockchip ISP1 Driver - Base driver 4e6938cc1SHelen Koike * 5e6938cc1SHelen Koike * Copyright (C) 2019 Collabora, Ltd. 6e6938cc1SHelen Koike * 7e6938cc1SHelen Koike * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd. 8e6938cc1SHelen Koike * Copyright (C) 2017 Rockchip Electronics Co., Ltd. 9e6938cc1SHelen Koike */ 10e6938cc1SHelen Koike 11e6938cc1SHelen Koike #include <linux/clk.h> 12e6938cc1SHelen Koike #include <linux/interrupt.h> 13e6938cc1SHelen Koike #include <linux/module.h> 14e6938cc1SHelen Koike #include <linux/of.h> 15e6938cc1SHelen Koike #include <linux/of_graph.h> 167c7e33b7SRob Herring #include <linux/platform_device.h> 17e6938cc1SHelen Koike #include <linux/pinctrl/consumer.h> 188082e2f4SPaul Elder #include <linux/pm_runtime.h> 19e6938cc1SHelen Koike #include <media/v4l2-fwnode.h> 2098bfd0cdSLaurent Pinchart #include <media/v4l2-mc.h> 21e6938cc1SHelen Koike 22e6938cc1SHelen Koike #include "rkisp1-common.h" 238082e2f4SPaul Elder #include "rkisp1-csi.h" 24e6938cc1SHelen Koike 25e6938cc1SHelen Koike /* 26e6938cc1SHelen Koike * ISP Details 27e6938cc1SHelen Koike * ----------- 28e6938cc1SHelen Koike * 29e6938cc1SHelen Koike * ISP Comprises with: 30e6938cc1SHelen Koike * MIPI serial camera interface 31e6938cc1SHelen Koike * Image Signal Processing 32e6938cc1SHelen Koike * Many Image Enhancement Blocks 33e6938cc1SHelen Koike * Crop 34e6938cc1SHelen Koike * Resizer 35e6938cc1SHelen Koike * RBG display ready image 36e6938cc1SHelen Koike * Image Rotation 37e6938cc1SHelen Koike * 38e6938cc1SHelen Koike * ISP Block Diagram 39e6938cc1SHelen Koike * ----------------- 40e6938cc1SHelen Koike * rkisp1-resizer.c rkisp1-capture.c 41e6938cc1SHelen Koike * |====================| |=======================| 42e6938cc1SHelen Koike * rkisp1-isp.c Main Picture Path 43e6938cc1SHelen Koike * |==========================| |===============================================| 44e6938cc1SHelen Koike * +-----------+ +--+--+--+--+ +--------+ +--------+ +-----------+ 45e6938cc1SHelen Koike * | | | | | | | | | | | | | 46e6938cc1SHelen Koike * +--------+ |\ | | | | | | | -->| Crop |->| RSZ |------------->| | 47e6938cc1SHelen Koike * | MIPI |--->| \ | | | | | | | | | | | | | | 48e6938cc1SHelen Koike * +--------+ | | | | |IE|IE|IE|IE| | +--------+ +--------+ | Memory | 49e6938cc1SHelen Koike * |MUX|--->| ISP |->|0 |1 |2 |3 |---+ | Interface | 50e6938cc1SHelen Koike * +--------+ | | | | | | | | | | +--------+ +--------+ +--------+ | | 51e6938cc1SHelen Koike * |Parallel|--->| / | | | | | | | | | | | | | | | | 52e6938cc1SHelen Koike * +--------+ |/ | | | | | | | -->| Crop |->| RSZ |->| RGB |->| | 53e6938cc1SHelen Koike * | | | | | | | | | | | | Rotate | | | 54e6938cc1SHelen Koike * +-----------+ +--+--+--+--+ +--------+ +--------+ +--------+ +-----------+ 55e6938cc1SHelen Koike * ^ 56e6938cc1SHelen Koike * +--------+ | |===============================================| 57e6938cc1SHelen Koike * | DMA |------------------------------------+ Self Picture Path 58e6938cc1SHelen Koike * +--------+ 59e6938cc1SHelen Koike * 60e6938cc1SHelen Koike * rkisp1-stats.c rkisp1-params.c 61e6938cc1SHelen Koike * |===============| |===============| 62e6938cc1SHelen Koike * +---------------+ +---------------+ 63e6938cc1SHelen Koike * | | | | 64e6938cc1SHelen Koike * | ISP | | ISP | 65e6938cc1SHelen Koike * | | | | 66e6938cc1SHelen Koike * +---------------+ +---------------+ 67e6938cc1SHelen Koike * 68e6938cc1SHelen Koike * 69e6938cc1SHelen Koike * Media Topology 70e6938cc1SHelen Koike * -------------- 7198bfd0cdSLaurent Pinchart * 72e6938cc1SHelen Koike * +----------+ +----------+ 7398bfd0cdSLaurent Pinchart * | Sensor 1 | | Sensor X | 74e6938cc1SHelen Koike * ------------ ... ------------ 75e6938cc1SHelen Koike * | 0 | | 0 | 7698bfd0cdSLaurent Pinchart * +----------+ +----------+ 7798bfd0cdSLaurent Pinchart * | | 7898bfd0cdSLaurent Pinchart * \----\ /----/ 7998bfd0cdSLaurent Pinchart * | | 8098bfd0cdSLaurent Pinchart * v v 8198bfd0cdSLaurent Pinchart * +-------------+ 8298bfd0cdSLaurent Pinchart * | 0 | 8398bfd0cdSLaurent Pinchart * --------------- 8498bfd0cdSLaurent Pinchart * | CSI-2 RX | 8598bfd0cdSLaurent Pinchart * --------------- +-----------+ 8698bfd0cdSLaurent Pinchart * | 1 | | params | 8798bfd0cdSLaurent Pinchart * +-------------+ | (output) | 8898bfd0cdSLaurent Pinchart * | +-----------+ 8998bfd0cdSLaurent Pinchart * v | 9098bfd0cdSLaurent Pinchart * +------+------+ | 9198bfd0cdSLaurent Pinchart * | 0 | 1 |<---------+ 9298bfd0cdSLaurent Pinchart * |------+------| 93e6938cc1SHelen Koike * | ISP | 94e6938cc1SHelen Koike * |------+------| 95e6938cc1SHelen Koike * +-------------| 2 | 3 |----------+ 96e6938cc1SHelen Koike * | +------+------+ | 97e6938cc1SHelen Koike * | | | 98e6938cc1SHelen Koike * v v v 99e6938cc1SHelen Koike * +- ---------+ +-----------+ +-----------+ 100e6938cc1SHelen Koike * | 0 | | 0 | | stats | 101e6938cc1SHelen Koike * ------------- ------------- | (capture) | 102e6938cc1SHelen Koike * | Resizer | | Resizer | +-----------+ 103e6938cc1SHelen Koike * ------------| ------------| 104e6938cc1SHelen Koike * | 1 | | 1 | 105e6938cc1SHelen Koike * +-----------+ +-----------+ 106e6938cc1SHelen Koike * | | 107e6938cc1SHelen Koike * v v 108e6938cc1SHelen Koike * +-----------+ +-----------+ 109e6938cc1SHelen Koike * | selfpath | | mainpath | 110e6938cc1SHelen Koike * | (capture) | | (capture) | 111e6938cc1SHelen Koike * +-----------+ +-----------+ 112e6938cc1SHelen Koike */ 113e6938cc1SHelen Koike 11408818e6aSHeiko Stuebner struct rkisp1_isr_data { 11508818e6aSHeiko Stuebner const char *name; 11608818e6aSHeiko Stuebner irqreturn_t (*isr)(int irq, void *ctx); 11707538746STomi Valkeinen u32 line_mask; 11808818e6aSHeiko Stuebner }; 11908818e6aSHeiko Stuebner 120e6938cc1SHelen Koike /* ---------------------------------------------------------------------------- 121e6938cc1SHelen Koike * Sensor DT bindings 122e6938cc1SHelen Koike */ 123e6938cc1SHelen Koike 124e6938cc1SHelen Koike static int rkisp1_subdev_notifier_bound(struct v4l2_async_notifier *notifier, 125e6938cc1SHelen Koike struct v4l2_subdev *sd, 126adb2dcd5SSakari Ailus struct v4l2_async_connection *asc) 127e6938cc1SHelen Koike { 128e6938cc1SHelen Koike struct rkisp1_device *rkisp1 = 129e6938cc1SHelen Koike container_of(notifier, struct rkisp1_device, notifier); 130e6938cc1SHelen Koike struct rkisp1_sensor_async *s_asd = 131adb2dcd5SSakari Ailus container_of(asc, struct rkisp1_sensor_async, asd); 132bc374e17SLaurent Pinchart int source_pad; 133f42f4558SPaul Elder int ret; 134e6938cc1SHelen Koike 135e6938cc1SHelen Koike s_asd->sd = sd; 136e6938cc1SHelen Koike 137b0b8ab68SLaurent Pinchart source_pad = media_entity_get_fwnode_pad(&sd->entity, s_asd->source_ep, 138bc374e17SLaurent Pinchart MEDIA_PAD_FL_SOURCE); 139bc374e17SLaurent Pinchart if (source_pad < 0) { 140bc374e17SLaurent Pinchart dev_err(rkisp1->dev, "failed to find source pad for %s\n", 141bc374e17SLaurent Pinchart sd->name); 142bc374e17SLaurent Pinchart return source_pad; 143bc374e17SLaurent Pinchart } 144bc374e17SLaurent Pinchart 145f42f4558SPaul Elder if (s_asd->port == 0) 14698bfd0cdSLaurent Pinchart return rkisp1_csi_link_sensor(rkisp1, sd, s_asd, source_pad); 147f42f4558SPaul Elder 148f42f4558SPaul Elder ret = media_create_pad_link(&sd->entity, source_pad, 149f42f4558SPaul Elder &rkisp1->isp.sd.entity, 150f42f4558SPaul Elder RKISP1_ISP_PAD_SINK_VIDEO, 151f42f4558SPaul Elder !s_asd->index ? MEDIA_LNK_FL_ENABLED : 0); 152f42f4558SPaul Elder if (ret) { 153f42f4558SPaul Elder dev_err(rkisp1->dev, "failed to link source pad of %s\n", 154f42f4558SPaul Elder sd->name); 155f42f4558SPaul Elder return ret; 156f42f4558SPaul Elder } 157f42f4558SPaul Elder 158f42f4558SPaul Elder return 0; 159e6938cc1SHelen Koike } 160e6938cc1SHelen Koike 161e6938cc1SHelen Koike static int rkisp1_subdev_notifier_complete(struct v4l2_async_notifier *notifier) 162e6938cc1SHelen Koike { 163e6938cc1SHelen Koike struct rkisp1_device *rkisp1 = 164e6938cc1SHelen Koike container_of(notifier, struct rkisp1_device, notifier); 165e6938cc1SHelen Koike 166cf7a8e24SLaurent Pinchart return v4l2_device_register_subdev_nodes(&rkisp1->v4l2_dev); 167e6938cc1SHelen Koike } 168e6938cc1SHelen Koike 169adb2dcd5SSakari Ailus static void rkisp1_subdev_notifier_destroy(struct v4l2_async_connection *asc) 170b0b8ab68SLaurent Pinchart { 171b0b8ab68SLaurent Pinchart struct rkisp1_sensor_async *rk_asd = 172adb2dcd5SSakari Ailus container_of(asc, struct rkisp1_sensor_async, asd); 173b0b8ab68SLaurent Pinchart 174b0b8ab68SLaurent Pinchart fwnode_handle_put(rk_asd->source_ep); 175b0b8ab68SLaurent Pinchart } 176b0b8ab68SLaurent Pinchart 177e6938cc1SHelen Koike static const struct v4l2_async_notifier_operations rkisp1_subdev_notifier_ops = { 178e6938cc1SHelen Koike .bound = rkisp1_subdev_notifier_bound, 179e6938cc1SHelen Koike .complete = rkisp1_subdev_notifier_complete, 180b0b8ab68SLaurent Pinchart .destroy = rkisp1_subdev_notifier_destroy, 181e6938cc1SHelen Koike }; 182e6938cc1SHelen Koike 183124b89f8SLaurent Pinchart static int rkisp1_subdev_notifier_register(struct rkisp1_device *rkisp1) 184e6938cc1SHelen Koike { 185e6938cc1SHelen Koike struct v4l2_async_notifier *ntf = &rkisp1->notifier; 1862452171eSPaul Elder struct fwnode_handle *fwnode = dev_fwnode(rkisp1->dev); 1872452171eSPaul Elder struct fwnode_handle *ep; 188bc374e17SLaurent Pinchart unsigned int index = 0; 1892452171eSPaul Elder int ret = 0; 190e6938cc1SHelen Koike 191b8ec754aSSakari Ailus v4l2_async_nf_init(ntf, &rkisp1->v4l2_dev); 192e6938cc1SHelen Koike 1932452171eSPaul Elder ntf->ops = &rkisp1_subdev_notifier_ops; 1942452171eSPaul Elder 1952452171eSPaul Elder fwnode_graph_for_each_endpoint(fwnode, ep) { 196f42f4558SPaul Elder struct fwnode_handle *port; 197f42f4558SPaul Elder struct v4l2_fwnode_endpoint vep = { }; 198b01edcbdSLaurent Pinchart struct rkisp1_sensor_async *rk_asd; 1992452171eSPaul Elder struct fwnode_handle *source; 200f42f4558SPaul Elder u32 reg = 0; 201e6938cc1SHelen Koike 202f42f4558SPaul Elder /* Select the bus type based on the port. */ 203f42f4558SPaul Elder port = fwnode_get_parent(ep); 204f42f4558SPaul Elder fwnode_property_read_u32(port, "reg", ®); 205f42f4558SPaul Elder fwnode_handle_put(port); 206f42f4558SPaul Elder 207f42f4558SPaul Elder switch (reg) { 208f42f4558SPaul Elder case 0: 2097d4f126fSLaurent Pinchart /* MIPI CSI-2 port */ 210*900f6676SLaurent Pinchart if (!rkisp1_has_feature(rkisp1, MIPI_CSI2)) { 2117d4f126fSLaurent Pinchart dev_err(rkisp1->dev, 2127d4f126fSLaurent Pinchart "internal CSI must be available for port 0\n"); 2137d4f126fSLaurent Pinchart ret = -EINVAL; 2147d4f126fSLaurent Pinchart break; 2157d4f126fSLaurent Pinchart } 2167d4f126fSLaurent Pinchart 217f42f4558SPaul Elder vep.bus_type = V4L2_MBUS_CSI2_DPHY; 218f42f4558SPaul Elder break; 219f42f4558SPaul Elder 220f42f4558SPaul Elder case 1: 221f42f4558SPaul Elder /* 222f42f4558SPaul Elder * Parallel port. The bus-type property in DT is 223f42f4558SPaul Elder * mandatory for port 1, it will be used to determine if 224f42f4558SPaul Elder * it's PARALLEL or BT656. 225f42f4558SPaul Elder */ 226f42f4558SPaul Elder vep.bus_type = V4L2_MBUS_UNKNOWN; 227f42f4558SPaul Elder break; 228f42f4558SPaul Elder } 229f42f4558SPaul Elder 230f42f4558SPaul Elder /* Parse the endpoint and validate the bus type. */ 231e6938cc1SHelen Koike ret = v4l2_fwnode_endpoint_parse(ep, &vep); 2322452171eSPaul Elder if (ret) { 2332452171eSPaul Elder dev_err(rkisp1->dev, "failed to parse endpoint %pfw\n", 2342452171eSPaul Elder ep); 2352452171eSPaul Elder break; 2362452171eSPaul Elder } 237e6938cc1SHelen Koike 238f42f4558SPaul Elder if (vep.base.port == 1) { 239f42f4558SPaul Elder if (vep.bus_type != V4L2_MBUS_PARALLEL && 240f42f4558SPaul Elder vep.bus_type != V4L2_MBUS_BT656) { 241f42f4558SPaul Elder dev_err(rkisp1->dev, 242f42f4558SPaul Elder "port 1 must be parallel or BT656\n"); 243f42f4558SPaul Elder ret = -EINVAL; 244f42f4558SPaul Elder break; 245f42f4558SPaul Elder } 246f42f4558SPaul Elder } 247f42f4558SPaul Elder 248f42f4558SPaul Elder /* Add the async subdev to the notifier. */ 249b0b8ab68SLaurent Pinchart source = fwnode_graph_get_remote_endpoint(ep); 250b0b8ab68SLaurent Pinchart if (!source) { 251b0b8ab68SLaurent Pinchart dev_err(rkisp1->dev, 252b0b8ab68SLaurent Pinchart "endpoint %pfw has no remote endpoint\n", 253b0b8ab68SLaurent Pinchart ep); 254b0b8ab68SLaurent Pinchart ret = -ENODEV; 2552452171eSPaul Elder break; 256b0b8ab68SLaurent Pinchart } 257b0b8ab68SLaurent Pinchart 258b0b8ab68SLaurent Pinchart rk_asd = v4l2_async_nf_add_fwnode(ntf, source, 259b0b8ab68SLaurent Pinchart struct rkisp1_sensor_async); 260b01edcbdSLaurent Pinchart if (IS_ERR(rk_asd)) { 2612452171eSPaul Elder fwnode_handle_put(source); 262b01edcbdSLaurent Pinchart ret = PTR_ERR(rk_asd); 2632452171eSPaul Elder break; 264e6938cc1SHelen Koike } 265e6938cc1SHelen Koike 266bc374e17SLaurent Pinchart rk_asd->index = index++; 267b0b8ab68SLaurent Pinchart rk_asd->source_ep = source; 268e6938cc1SHelen Koike rk_asd->mbus_type = vep.bus_type; 269f42f4558SPaul Elder rk_asd->port = vep.base.port; 270f42f4558SPaul Elder 271f42f4558SPaul Elder if (vep.bus_type == V4L2_MBUS_CSI2_DPHY) { 272e6938cc1SHelen Koike rk_asd->mbus_flags = vep.bus.mipi_csi2.flags; 273e6938cc1SHelen Koike rk_asd->lanes = vep.bus.mipi_csi2.num_data_lanes; 274f42f4558SPaul Elder } else { 275f42f4558SPaul Elder rk_asd->mbus_flags = vep.bus.parallel.flags; 276f42f4558SPaul Elder } 277e6938cc1SHelen Koike 278f42f4558SPaul Elder dev_dbg(rkisp1->dev, "registered ep id %d, bus type %u, %u lanes\n", 279f42f4558SPaul Elder vep.base.id, rk_asd->mbus_type, rk_asd->lanes); 2802452171eSPaul Elder } 281e6938cc1SHelen Koike 2822452171eSPaul Elder if (ret) { 283e6938cc1SHelen Koike fwnode_handle_put(ep); 2843c8c1539SSakari Ailus v4l2_async_nf_cleanup(ntf); 285e6938cc1SHelen Koike return ret; 286e6938cc1SHelen Koike } 287e6938cc1SHelen Koike 2882452171eSPaul Elder if (!index) 289e6938cc1SHelen Koike dev_dbg(rkisp1->dev, "no remote subdevice found\n"); 2902452171eSPaul Elder 291b8ec754aSSakari Ailus ret = v4l2_async_nf_register(ntf); 292e6938cc1SHelen Koike if (ret) { 2933c8c1539SSakari Ailus v4l2_async_nf_cleanup(ntf); 294e6938cc1SHelen Koike return ret; 295e6938cc1SHelen Koike } 2962452171eSPaul Elder 297e6938cc1SHelen Koike return 0; 298e6938cc1SHelen Koike } 299e6938cc1SHelen Koike 300e6938cc1SHelen Koike /* ---------------------------------------------------------------------------- 301e6938cc1SHelen Koike * Power 302e6938cc1SHelen Koike */ 303e6938cc1SHelen Koike 304e6938cc1SHelen Koike static int __maybe_unused rkisp1_runtime_suspend(struct device *dev) 305e6938cc1SHelen Koike { 306e6938cc1SHelen Koike struct rkisp1_device *rkisp1 = dev_get_drvdata(dev); 307e6938cc1SHelen Koike 308e6938cc1SHelen Koike clk_bulk_disable_unprepare(rkisp1->clk_size, rkisp1->clks); 309e6938cc1SHelen Koike return pinctrl_pm_select_sleep_state(dev); 310e6938cc1SHelen Koike } 311e6938cc1SHelen Koike 312e6938cc1SHelen Koike static int __maybe_unused rkisp1_runtime_resume(struct device *dev) 313e6938cc1SHelen Koike { 314e6938cc1SHelen Koike struct rkisp1_device *rkisp1 = dev_get_drvdata(dev); 315e6938cc1SHelen Koike int ret; 316e6938cc1SHelen Koike 317e6938cc1SHelen Koike ret = pinctrl_pm_select_default_state(dev); 318e6938cc1SHelen Koike if (ret) 319e6938cc1SHelen Koike return ret; 320e6938cc1SHelen Koike ret = clk_bulk_prepare_enable(rkisp1->clk_size, rkisp1->clks); 321e6938cc1SHelen Koike if (ret) 322e6938cc1SHelen Koike return ret; 323e6938cc1SHelen Koike 324e6938cc1SHelen Koike return 0; 325e6938cc1SHelen Koike } 326e6938cc1SHelen Koike 327e6938cc1SHelen Koike static const struct dev_pm_ops rkisp1_pm_ops = { 328e6938cc1SHelen Koike SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 329e6938cc1SHelen Koike pm_runtime_force_resume) 330e6938cc1SHelen Koike SET_RUNTIME_PM_OPS(rkisp1_runtime_suspend, rkisp1_runtime_resume, NULL) 331e6938cc1SHelen Koike }; 332e6938cc1SHelen Koike 333e6938cc1SHelen Koike /* ---------------------------------------------------------------------------- 334e6938cc1SHelen Koike * Core 335e6938cc1SHelen Koike */ 336e6938cc1SHelen Koike 337cf7a8e24SLaurent Pinchart static int rkisp1_create_links(struct rkisp1_device *rkisp1) 338cf7a8e24SLaurent Pinchart { 339cf7a8e24SLaurent Pinchart unsigned int i; 340cf7a8e24SLaurent Pinchart int ret; 341cf7a8e24SLaurent Pinchart 342*900f6676SLaurent Pinchart if (rkisp1_has_feature(rkisp1, MIPI_CSI2)) { 34398bfd0cdSLaurent Pinchart /* Link the CSI receiver to the ISP. */ 3447d4f126fSLaurent Pinchart ret = media_create_pad_link(&rkisp1->csi.sd.entity, 3457d4f126fSLaurent Pinchart RKISP1_CSI_PAD_SRC, 34698bfd0cdSLaurent Pinchart &rkisp1->isp.sd.entity, 34798bfd0cdSLaurent Pinchart RKISP1_ISP_PAD_SINK_VIDEO, 34898bfd0cdSLaurent Pinchart MEDIA_LNK_FL_ENABLED); 34998bfd0cdSLaurent Pinchart if (ret) 35098bfd0cdSLaurent Pinchart return ret; 3517d4f126fSLaurent Pinchart } 35298bfd0cdSLaurent Pinchart 353cf7a8e24SLaurent Pinchart /* create ISP->RSZ->CAP links */ 354cf7a8e24SLaurent Pinchart for (i = 0; i < 2; i++) { 355cf7a8e24SLaurent Pinchart struct media_entity *resizer = 356cf7a8e24SLaurent Pinchart &rkisp1->resizer_devs[i].sd.entity; 357cf7a8e24SLaurent Pinchart struct media_entity *capture = 358cf7a8e24SLaurent Pinchart &rkisp1->capture_devs[i].vnode.vdev.entity; 359cf7a8e24SLaurent Pinchart 360cf7a8e24SLaurent Pinchart ret = media_create_pad_link(&rkisp1->isp.sd.entity, 361cf7a8e24SLaurent Pinchart RKISP1_ISP_PAD_SOURCE_VIDEO, 362cf7a8e24SLaurent Pinchart resizer, RKISP1_RSZ_PAD_SINK, 363cf7a8e24SLaurent Pinchart MEDIA_LNK_FL_ENABLED); 364cf7a8e24SLaurent Pinchart if (ret) 365cf7a8e24SLaurent Pinchart return ret; 366cf7a8e24SLaurent Pinchart 367cf7a8e24SLaurent Pinchart ret = media_create_pad_link(resizer, RKISP1_RSZ_PAD_SRC, 368cf7a8e24SLaurent Pinchart capture, 0, 369cf7a8e24SLaurent Pinchart MEDIA_LNK_FL_ENABLED | 370cf7a8e24SLaurent Pinchart MEDIA_LNK_FL_IMMUTABLE); 371cf7a8e24SLaurent Pinchart if (ret) 372cf7a8e24SLaurent Pinchart return ret; 373cf7a8e24SLaurent Pinchart } 374cf7a8e24SLaurent Pinchart 375cf7a8e24SLaurent Pinchart /* params links */ 376cf7a8e24SLaurent Pinchart ret = media_create_pad_link(&rkisp1->params.vnode.vdev.entity, 0, 377cf7a8e24SLaurent Pinchart &rkisp1->isp.sd.entity, 378cf7a8e24SLaurent Pinchart RKISP1_ISP_PAD_SINK_PARAMS, 379cf7a8e24SLaurent Pinchart MEDIA_LNK_FL_ENABLED | 380cf7a8e24SLaurent Pinchart MEDIA_LNK_FL_IMMUTABLE); 381cf7a8e24SLaurent Pinchart if (ret) 382cf7a8e24SLaurent Pinchart return ret; 383cf7a8e24SLaurent Pinchart 384cf7a8e24SLaurent Pinchart /* 3A stats links */ 385cf7a8e24SLaurent Pinchart return media_create_pad_link(&rkisp1->isp.sd.entity, 386cf7a8e24SLaurent Pinchart RKISP1_ISP_PAD_SOURCE_STATS, 387cf7a8e24SLaurent Pinchart &rkisp1->stats.vnode.vdev.entity, 0, 388cf7a8e24SLaurent Pinchart MEDIA_LNK_FL_ENABLED | 389cf7a8e24SLaurent Pinchart MEDIA_LNK_FL_IMMUTABLE); 390cf7a8e24SLaurent Pinchart } 391cf7a8e24SLaurent Pinchart 3926ff02276SLaurent Pinchart static void rkisp1_entities_unregister(struct rkisp1_device *rkisp1) 3936ff02276SLaurent Pinchart { 394*900f6676SLaurent Pinchart if (rkisp1_has_feature(rkisp1, MIPI_CSI2)) 395b298f059SPaul Elder rkisp1_csi_unregister(rkisp1); 3966ff02276SLaurent Pinchart rkisp1_params_unregister(rkisp1); 3976ff02276SLaurent Pinchart rkisp1_stats_unregister(rkisp1); 3986ff02276SLaurent Pinchart rkisp1_capture_devs_unregister(rkisp1); 3996ff02276SLaurent Pinchart rkisp1_resizer_devs_unregister(rkisp1); 4006ff02276SLaurent Pinchart rkisp1_isp_unregister(rkisp1); 4016ff02276SLaurent Pinchart } 4026ff02276SLaurent Pinchart 403e6938cc1SHelen Koike static int rkisp1_entities_register(struct rkisp1_device *rkisp1) 404e6938cc1SHelen Koike { 405e6938cc1SHelen Koike int ret; 406e6938cc1SHelen Koike 407e6938cc1SHelen Koike ret = rkisp1_isp_register(rkisp1); 408e6938cc1SHelen Koike if (ret) 4096ff02276SLaurent Pinchart goto error; 410e6938cc1SHelen Koike 411e6938cc1SHelen Koike ret = rkisp1_resizer_devs_register(rkisp1); 412e6938cc1SHelen Koike if (ret) 4136ff02276SLaurent Pinchart goto error; 414e6938cc1SHelen Koike 415e6938cc1SHelen Koike ret = rkisp1_capture_devs_register(rkisp1); 416e6938cc1SHelen Koike if (ret) 4176ff02276SLaurent Pinchart goto error; 418e6938cc1SHelen Koike 419e6938cc1SHelen Koike ret = rkisp1_stats_register(rkisp1); 420e6938cc1SHelen Koike if (ret) 4216ff02276SLaurent Pinchart goto error; 422e6938cc1SHelen Koike 423e6938cc1SHelen Koike ret = rkisp1_params_register(rkisp1); 424e6938cc1SHelen Koike if (ret) 4256ff02276SLaurent Pinchart goto error; 426e6938cc1SHelen Koike 427*900f6676SLaurent Pinchart if (rkisp1_has_feature(rkisp1, MIPI_CSI2)) { 428b298f059SPaul Elder ret = rkisp1_csi_register(rkisp1); 429b298f059SPaul Elder if (ret) 430b298f059SPaul Elder goto error; 4317d4f126fSLaurent Pinchart } 432b298f059SPaul Elder 433cf7a8e24SLaurent Pinchart ret = rkisp1_create_links(rkisp1); 434cf7a8e24SLaurent Pinchart if (ret) 435cf7a8e24SLaurent Pinchart goto error; 436cf7a8e24SLaurent Pinchart 437e6938cc1SHelen Koike return 0; 4386ff02276SLaurent Pinchart 4396ff02276SLaurent Pinchart error: 4406ff02276SLaurent Pinchart rkisp1_entities_unregister(rkisp1); 441e6938cc1SHelen Koike return ret; 442e6938cc1SHelen Koike } 443e6938cc1SHelen Koike 444e6938cc1SHelen Koike static irqreturn_t rkisp1_isr(int irq, void *ctx) 445e6938cc1SHelen Koike { 4463eb7910eSTomi Valkeinen irqreturn_t ret = IRQ_NONE; 4473eb7910eSTomi Valkeinen 448e6938cc1SHelen Koike /* 449e6938cc1SHelen Koike * Call rkisp1_capture_isr() first to handle the frame that 450e6938cc1SHelen Koike * potentially completed using the current frame_sequence number before 451e6938cc1SHelen Koike * it is potentially incremented by rkisp1_isp_isr() in the vertical 452e6938cc1SHelen Koike * sync. 453e6938cc1SHelen Koike */ 454e6938cc1SHelen Koike 4553eb7910eSTomi Valkeinen if (rkisp1_capture_isr(irq, ctx) == IRQ_HANDLED) 4563eb7910eSTomi Valkeinen ret = IRQ_HANDLED; 4573eb7910eSTomi Valkeinen 4583eb7910eSTomi Valkeinen if (rkisp1_isp_isr(irq, ctx) == IRQ_HANDLED) 4593eb7910eSTomi Valkeinen ret = IRQ_HANDLED; 4603eb7910eSTomi Valkeinen 4613eb7910eSTomi Valkeinen if (rkisp1_csi_isr(irq, ctx) == IRQ_HANDLED) 4623eb7910eSTomi Valkeinen ret = IRQ_HANDLED; 4633eb7910eSTomi Valkeinen 4643eb7910eSTomi Valkeinen return ret; 465e6938cc1SHelen Koike } 466e6938cc1SHelen Koike 467ecf8d36fSHeiko Stuebner static const char * const px30_isp_clks[] = { 468ecf8d36fSHeiko Stuebner "isp", 469ecf8d36fSHeiko Stuebner "aclk", 470ecf8d36fSHeiko Stuebner "hclk", 471ecf8d36fSHeiko Stuebner "pclk", 472ecf8d36fSHeiko Stuebner }; 473ecf8d36fSHeiko Stuebner 474ecf8d36fSHeiko Stuebner static const struct rkisp1_isr_data px30_isp_isrs[] = { 47507538746STomi Valkeinen { "isp", rkisp1_isp_isr, BIT(RKISP1_IRQ_ISP) }, 47607538746STomi Valkeinen { "mi", rkisp1_capture_isr, BIT(RKISP1_IRQ_MI) }, 47707538746STomi Valkeinen { "mipi", rkisp1_csi_isr, BIT(RKISP1_IRQ_MIPI) }, 478ecf8d36fSHeiko Stuebner }; 479ecf8d36fSHeiko Stuebner 480cdce5b95SLaurent Pinchart static const struct rkisp1_info px30_isp_info = { 481ecf8d36fSHeiko Stuebner .clks = px30_isp_clks, 482ecf8d36fSHeiko Stuebner .clk_size = ARRAY_SIZE(px30_isp_clks), 483ecf8d36fSHeiko Stuebner .isrs = px30_isp_isrs, 484ecf8d36fSHeiko Stuebner .isr_size = ARRAY_SIZE(px30_isp_isrs), 485ecf8d36fSHeiko Stuebner .isp_ver = RKISP1_V12, 486f1b8f171SLaurent Pinchart .features = RKISP1_FEATURE_MIPI_CSI2, 487ecf8d36fSHeiko Stuebner }; 488ecf8d36fSHeiko Stuebner 489e6938cc1SHelen Koike static const char * const rk3399_isp_clks[] = { 490e6938cc1SHelen Koike "isp", 491e6938cc1SHelen Koike "aclk", 492e6938cc1SHelen Koike "hclk", 493e6938cc1SHelen Koike }; 494e6938cc1SHelen Koike 49508818e6aSHeiko Stuebner static const struct rkisp1_isr_data rk3399_isp_isrs[] = { 49607538746STomi Valkeinen { NULL, rkisp1_isr, BIT(RKISP1_IRQ_ISP) | BIT(RKISP1_IRQ_MI) | BIT(RKISP1_IRQ_MIPI) }, 49708818e6aSHeiko Stuebner }; 49808818e6aSHeiko Stuebner 499cdce5b95SLaurent Pinchart static const struct rkisp1_info rk3399_isp_info = { 500e6938cc1SHelen Koike .clks = rk3399_isp_clks, 50108818e6aSHeiko Stuebner .clk_size = ARRAY_SIZE(rk3399_isp_clks), 50208818e6aSHeiko Stuebner .isrs = rk3399_isp_isrs, 50308818e6aSHeiko Stuebner .isr_size = ARRAY_SIZE(rk3399_isp_isrs), 504fc672d80SHeiko Stuebner .isp_ver = RKISP1_V10, 505f1b8f171SLaurent Pinchart .features = RKISP1_FEATURE_MIPI_CSI2, 506e6938cc1SHelen Koike }; 507e6938cc1SHelen Koike 508e6938cc1SHelen Koike static const struct of_device_id rkisp1_of_match[] = { 509e6938cc1SHelen Koike { 510ecf8d36fSHeiko Stuebner .compatible = "rockchip,px30-cif-isp", 511cdce5b95SLaurent Pinchart .data = &px30_isp_info, 512ecf8d36fSHeiko Stuebner }, 513ecf8d36fSHeiko Stuebner { 514e6938cc1SHelen Koike .compatible = "rockchip,rk3399-cif-isp", 515cdce5b95SLaurent Pinchart .data = &rk3399_isp_info, 516e6938cc1SHelen Koike }, 517e6938cc1SHelen Koike {}, 518e6938cc1SHelen Koike }; 519e6938cc1SHelen Koike MODULE_DEVICE_TABLE(of, rkisp1_of_match); 520e6938cc1SHelen Koike 521e6938cc1SHelen Koike static int rkisp1_probe(struct platform_device *pdev) 522e6938cc1SHelen Koike { 523cdce5b95SLaurent Pinchart const struct rkisp1_info *info; 524e6938cc1SHelen Koike struct device *dev = &pdev->dev; 525e6938cc1SHelen Koike struct rkisp1_device *rkisp1; 526e6938cc1SHelen Koike struct v4l2_device *v4l2_dev; 527e6938cc1SHelen Koike unsigned int i; 528e6938cc1SHelen Koike int ret, irq; 529196179c5SLaurent Pinchart u32 cif_id; 530e6938cc1SHelen Koike 531e6938cc1SHelen Koike rkisp1 = devm_kzalloc(dev, sizeof(*rkisp1), GFP_KERNEL); 532e6938cc1SHelen Koike if (!rkisp1) 533e6938cc1SHelen Koike return -ENOMEM; 534e6938cc1SHelen Koike 5359125aee7SPaul Elder info = of_device_get_match_data(dev); 5369125aee7SPaul Elder rkisp1->info = info; 5379125aee7SPaul Elder 538e6938cc1SHelen Koike dev_set_drvdata(dev, rkisp1); 539e6938cc1SHelen Koike rkisp1->dev = dev; 540e6938cc1SHelen Koike 541e6938cc1SHelen Koike mutex_init(&rkisp1->stream_lock); 542e6938cc1SHelen Koike 543e6938cc1SHelen Koike rkisp1->base_addr = devm_platform_ioremap_resource(pdev, 0); 544e6938cc1SHelen Koike if (IS_ERR(rkisp1->base_addr)) 545e6938cc1SHelen Koike return PTR_ERR(rkisp1->base_addr); 546e6938cc1SHelen Koike 54707538746STomi Valkeinen for (unsigned int il = 0; il < ARRAY_SIZE(rkisp1->irqs); ++il) 54807538746STomi Valkeinen rkisp1->irqs[il] = -1; 54907538746STomi Valkeinen 550cdce5b95SLaurent Pinchart for (i = 0; i < info->isr_size; i++) { 551cdce5b95SLaurent Pinchart irq = info->isrs[i].name 552cdce5b95SLaurent Pinchart ? platform_get_irq_byname(pdev, info->isrs[i].name) 553fd83ef8fSLaurent Pinchart : platform_get_irq(pdev, i); 554e6938cc1SHelen Koike if (irq < 0) 555e6938cc1SHelen Koike return irq; 556e6938cc1SHelen Koike 55707538746STomi Valkeinen for (unsigned int il = 0; il < ARRAY_SIZE(rkisp1->irqs); ++il) { 55807538746STomi Valkeinen if (info->isrs[i].line_mask & BIT(il)) 55907538746STomi Valkeinen rkisp1->irqs[il] = irq; 56007538746STomi Valkeinen } 56107538746STomi Valkeinen 56285d2a31fSTomi Valkeinen ret = devm_request_irq(dev, irq, info->isrs[i].isr, 0, 563e6938cc1SHelen Koike dev_driver_string(dev), dev); 564e6938cc1SHelen Koike if (ret) { 565e6938cc1SHelen Koike dev_err(dev, "request irq failed: %d\n", ret); 566e6938cc1SHelen Koike return ret; 567e6938cc1SHelen Koike } 56808818e6aSHeiko Stuebner } 569e6938cc1SHelen Koike 570cdce5b95SLaurent Pinchart for (i = 0; i < info->clk_size; i++) 571cdce5b95SLaurent Pinchart rkisp1->clks[i].id = info->clks[i]; 572cdce5b95SLaurent Pinchart ret = devm_clk_bulk_get(dev, info->clk_size, rkisp1->clks); 573e6938cc1SHelen Koike if (ret) 574e6938cc1SHelen Koike return ret; 575cdce5b95SLaurent Pinchart rkisp1->clk_size = info->clk_size; 576e6938cc1SHelen Koike 577e6938cc1SHelen Koike pm_runtime_enable(&pdev->dev); 578e6938cc1SHelen Koike 579196179c5SLaurent Pinchart ret = pm_runtime_resume_and_get(&pdev->dev); 580196179c5SLaurent Pinchart if (ret) 581196179c5SLaurent Pinchart goto err_pm_runtime_disable; 582196179c5SLaurent Pinchart 583196179c5SLaurent Pinchart cif_id = rkisp1_read(rkisp1, RKISP1_CIF_VI_ID); 584196179c5SLaurent Pinchart dev_dbg(rkisp1->dev, "CIF_ID 0x%08x\n", cif_id); 585196179c5SLaurent Pinchart 586196179c5SLaurent Pinchart pm_runtime_put(&pdev->dev); 587196179c5SLaurent Pinchart 588cdce5b95SLaurent Pinchart rkisp1->media_dev.hw_revision = info->isp_ver; 589e6938cc1SHelen Koike strscpy(rkisp1->media_dev.model, RKISP1_DRIVER_NAME, 590e6938cc1SHelen Koike sizeof(rkisp1->media_dev.model)); 591e6938cc1SHelen Koike rkisp1->media_dev.dev = &pdev->dev; 592e6938cc1SHelen Koike strscpy(rkisp1->media_dev.bus_info, RKISP1_BUS_INFO, 593e6938cc1SHelen Koike sizeof(rkisp1->media_dev.bus_info)); 594e6938cc1SHelen Koike media_device_init(&rkisp1->media_dev); 595e6938cc1SHelen Koike 596e6938cc1SHelen Koike v4l2_dev = &rkisp1->v4l2_dev; 597e6938cc1SHelen Koike v4l2_dev->mdev = &rkisp1->media_dev; 598e6938cc1SHelen Koike strscpy(v4l2_dev->name, RKISP1_DRIVER_NAME, sizeof(v4l2_dev->name)); 599e6938cc1SHelen Koike 600e6938cc1SHelen Koike ret = v4l2_device_register(rkisp1->dev, &rkisp1->v4l2_dev); 601e6938cc1SHelen Koike if (ret) 602452f604aSTomi Valkeinen goto err_media_dev_cleanup; 603e6938cc1SHelen Koike 604e6938cc1SHelen Koike ret = media_device_register(&rkisp1->media_dev); 605e6938cc1SHelen Koike if (ret) { 606e6938cc1SHelen Koike dev_err(dev, "Failed to register media device: %d\n", ret); 607e6938cc1SHelen Koike goto err_unreg_v4l2_dev; 608e6938cc1SHelen Koike } 609e6938cc1SHelen Koike 6107d4f126fSLaurent Pinchart if (rkisp1->info->features & RKISP1_FEATURE_MIPI_CSI2) { 6118082e2f4SPaul Elder ret = rkisp1_csi_init(rkisp1); 612e6938cc1SHelen Koike if (ret) 613e6938cc1SHelen Koike goto err_unreg_media_dev; 6147d4f126fSLaurent Pinchart } 615e6938cc1SHelen Koike 6168082e2f4SPaul Elder ret = rkisp1_entities_register(rkisp1); 6178082e2f4SPaul Elder if (ret) 6188082e2f4SPaul Elder goto err_cleanup_csi; 6198082e2f4SPaul Elder 62098bfd0cdSLaurent Pinchart ret = rkisp1_subdev_notifier_register(rkisp1); 62198bfd0cdSLaurent Pinchart if (ret) 62298bfd0cdSLaurent Pinchart goto err_unreg_entities; 62398bfd0cdSLaurent Pinchart 624e6938cc1SHelen Koike rkisp1_debug_init(rkisp1); 625e6938cc1SHelen Koike 626e6938cc1SHelen Koike return 0; 627e6938cc1SHelen Koike 62898bfd0cdSLaurent Pinchart err_unreg_entities: 62998bfd0cdSLaurent Pinchart rkisp1_entities_unregister(rkisp1); 6308082e2f4SPaul Elder err_cleanup_csi: 631*900f6676SLaurent Pinchart if (rkisp1_has_feature(rkisp1, MIPI_CSI2)) 6328082e2f4SPaul Elder rkisp1_csi_cleanup(rkisp1); 633e6938cc1SHelen Koike err_unreg_media_dev: 634e6938cc1SHelen Koike media_device_unregister(&rkisp1->media_dev); 635e6938cc1SHelen Koike err_unreg_v4l2_dev: 636e6938cc1SHelen Koike v4l2_device_unregister(&rkisp1->v4l2_dev); 637452f604aSTomi Valkeinen err_media_dev_cleanup: 638452f604aSTomi Valkeinen media_device_cleanup(&rkisp1->media_dev); 63913c98102SLaurent Pinchart err_pm_runtime_disable: 640e6938cc1SHelen Koike pm_runtime_disable(&pdev->dev); 641e6938cc1SHelen Koike return ret; 642e6938cc1SHelen Koike } 643e6938cc1SHelen Koike 644073dcc08SUwe Kleine-König static void rkisp1_remove(struct platform_device *pdev) 645e6938cc1SHelen Koike { 646e6938cc1SHelen Koike struct rkisp1_device *rkisp1 = platform_get_drvdata(pdev); 647e6938cc1SHelen Koike 6483c8c1539SSakari Ailus v4l2_async_nf_unregister(&rkisp1->notifier); 6493c8c1539SSakari Ailus v4l2_async_nf_cleanup(&rkisp1->notifier); 650e6938cc1SHelen Koike 6516ff02276SLaurent Pinchart rkisp1_entities_unregister(rkisp1); 652*900f6676SLaurent Pinchart if (rkisp1_has_feature(rkisp1, MIPI_CSI2)) 6538082e2f4SPaul Elder rkisp1_csi_cleanup(rkisp1); 6548682037dSLaurent Pinchart rkisp1_debug_cleanup(rkisp1); 655e6938cc1SHelen Koike 656e6938cc1SHelen Koike media_device_unregister(&rkisp1->media_dev); 657e6938cc1SHelen Koike v4l2_device_unregister(&rkisp1->v4l2_dev); 658e6938cc1SHelen Koike 659452f604aSTomi Valkeinen media_device_cleanup(&rkisp1->media_dev); 660452f604aSTomi Valkeinen 661e6938cc1SHelen Koike pm_runtime_disable(&pdev->dev); 662e6938cc1SHelen Koike } 663e6938cc1SHelen Koike 664e6938cc1SHelen Koike static struct platform_driver rkisp1_drv = { 665e6938cc1SHelen Koike .driver = { 666e6938cc1SHelen Koike .name = RKISP1_DRIVER_NAME, 667e6938cc1SHelen Koike .of_match_table = of_match_ptr(rkisp1_of_match), 668e6938cc1SHelen Koike .pm = &rkisp1_pm_ops, 669e6938cc1SHelen Koike }, 670e6938cc1SHelen Koike .probe = rkisp1_probe, 671073dcc08SUwe Kleine-König .remove_new = rkisp1_remove, 672e6938cc1SHelen Koike }; 673e6938cc1SHelen Koike 674e6938cc1SHelen Koike module_platform_driver(rkisp1_drv); 675e6938cc1SHelen Koike MODULE_DESCRIPTION("Rockchip ISP1 platform driver"); 676e6938cc1SHelen Koike MODULE_LICENSE("Dual MIT/GPL"); 677