533 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2021 Renesas Electronics Corp.4 *5 * Driver for Renesas R-Car ISP Channel Selector6 *7 * The ISP hardware is capable of more than just channel selection, features8 * such as demosaicing, white balance control and color space conversion are9 * also possible. These more advanced features are not supported by the driver10 * due to lack of documentation.11 */12 13#include <linux/module.h>14#include <linux/mutex.h>15#include <linux/of.h>16#include <linux/platform_device.h>17#include <linux/pm_runtime.h>18#include <linux/reset.h>19 20#include <media/mipi-csi2.h>21#include <media/v4l2-subdev.h>22 23#define ISPINPUTSEL0_REG 0x000824#define ISPINPUTSEL0_SEL_CSI0 BIT(31)25 26#define ISPSTART_REG 0x001427#define ISPSTART_START 0xffff28#define ISPSTART_STOP 0x000029 30#define ISPPROCMODE_DT_REG(n) (0x1100 + (0x4 * (n)))31#define ISPPROCMODE_DT_PROC_MODE_VC3(pm) (((pm) & 0x3f) << 24)32#define ISPPROCMODE_DT_PROC_MODE_VC2(pm) (((pm) & 0x3f) << 16)33#define ISPPROCMODE_DT_PROC_MODE_VC1(pm) (((pm) & 0x3f) << 8)34#define ISPPROCMODE_DT_PROC_MODE_VC0(pm) ((pm) & 0x3f)35 36#define ISPCS_FILTER_ID_CH_REG(n) (0x3000 + (0x0100 * (n)))37 38#define ISPCS_DT_CODE03_CH_REG(n) (0x3008 + (0x100 * (n)))39#define ISPCS_DT_CODE03_EN3 BIT(31)40#define ISPCS_DT_CODE03_DT3(dt) (((dt) & 0x3f) << 24)41#define ISPCS_DT_CODE03_EN2 BIT(23)42#define ISPCS_DT_CODE03_DT2(dt) (((dt) & 0x3f) << 16)43#define ISPCS_DT_CODE03_EN1 BIT(15)44#define ISPCS_DT_CODE03_DT1(dt) (((dt) & 0x3f) << 8)45#define ISPCS_DT_CODE03_EN0 BIT(7)46#define ISPCS_DT_CODE03_DT0(dt) ((dt) & 0x3f)47 48struct rcar_isp_format {49 u32 code;50 unsigned int datatype;51 unsigned int procmode;52};53 54static const struct rcar_isp_format rcar_isp_formats[] = {55 {56 .code = MEDIA_BUS_FMT_RGB888_1X24,57 .datatype = MIPI_CSI2_DT_RGB888,58 .procmode = 0x1559 }, {60 .code = MEDIA_BUS_FMT_Y10_1X10,61 .datatype = MIPI_CSI2_DT_RAW10,62 .procmode = 0x10,63 }, {64 .code = MEDIA_BUS_FMT_UYVY8_1X16,65 .datatype = MIPI_CSI2_DT_YUV422_8B,66 .procmode = 0x0c,67 }, {68 .code = MEDIA_BUS_FMT_YUYV8_1X16,69 .datatype = MIPI_CSI2_DT_YUV422_8B,70 .procmode = 0x0c,71 }, {72 .code = MEDIA_BUS_FMT_UYVY8_2X8,73 .datatype = MIPI_CSI2_DT_YUV422_8B,74 .procmode = 0x0c,75 }, {76 .code = MEDIA_BUS_FMT_YUYV10_2X10,77 .datatype = MIPI_CSI2_DT_YUV422_8B,78 .procmode = 0x0c,79 },80};81 82static const struct rcar_isp_format *risp_code_to_fmt(unsigned int code)83{84 unsigned int i;85 86 for (i = 0; i < ARRAY_SIZE(rcar_isp_formats); i++) {87 if (rcar_isp_formats[i].code == code)88 return &rcar_isp_formats[i];89 }90 91 return NULL;92}93 94enum rcar_isp_input {95 RISP_CSI_INPUT0,96 RISP_CSI_INPUT1,97};98 99enum rcar_isp_pads {100 RCAR_ISP_SINK,101 RCAR_ISP_PORT0,102 RCAR_ISP_PORT1,103 RCAR_ISP_PORT2,104 RCAR_ISP_PORT3,105 RCAR_ISP_PORT4,106 RCAR_ISP_PORT5,107 RCAR_ISP_PORT6,108 RCAR_ISP_PORT7,109 RCAR_ISP_NUM_PADS,110};111 112struct rcar_isp {113 struct device *dev;114 void __iomem *base;115 struct reset_control *rstc;116 117 enum rcar_isp_input csi_input;118 119 struct v4l2_subdev subdev;120 struct media_pad pads[RCAR_ISP_NUM_PADS];121 122 struct v4l2_async_notifier notifier;123 struct v4l2_subdev *remote;124 125 struct mutex lock; /* Protects mf and stream_count. */126 struct v4l2_mbus_framefmt mf;127 int stream_count;128};129 130static inline struct rcar_isp *sd_to_isp(struct v4l2_subdev *sd)131{132 return container_of(sd, struct rcar_isp, subdev);133}134 135static inline struct rcar_isp *notifier_to_isp(struct v4l2_async_notifier *n)136{137 return container_of(n, struct rcar_isp, notifier);138}139 140static void risp_write(struct rcar_isp *isp, u32 offset, u32 value)141{142 iowrite32(value, isp->base + offset);143}144 145static u32 risp_read(struct rcar_isp *isp, u32 offset)146{147 return ioread32(isp->base + offset);148}149 150static int risp_power_on(struct rcar_isp *isp)151{152 int ret;153 154 ret = pm_runtime_resume_and_get(isp->dev);155 if (ret < 0)156 return ret;157 158 ret = reset_control_deassert(isp->rstc);159 if (ret < 0) {160 pm_runtime_put(isp->dev);161 return ret;162 }163 164 return 0;165}166 167static void risp_power_off(struct rcar_isp *isp)168{169 reset_control_assert(isp->rstc);170 pm_runtime_put(isp->dev);171}172 173static int risp_start(struct rcar_isp *isp)174{175 const struct rcar_isp_format *format;176 unsigned int vc;177 u32 sel_csi = 0;178 int ret;179 180 format = risp_code_to_fmt(isp->mf.code);181 if (!format) {182 dev_err(isp->dev, "Unsupported bus format\n");183 return -EINVAL;184 }185 186 ret = risp_power_on(isp);187 if (ret) {188 dev_err(isp->dev, "Failed to power on ISP\n");189 return ret;190 }191 192 /* Select CSI-2 input source. */193 if (isp->csi_input == RISP_CSI_INPUT1)194 sel_csi = ISPINPUTSEL0_SEL_CSI0;195 196 risp_write(isp, ISPINPUTSEL0_REG,197 risp_read(isp, ISPINPUTSEL0_REG) | sel_csi);198 199 /* Configure Channel Selector. */200 for (vc = 0; vc < 4; vc++) {201 u8 ch = vc + 4;202 u8 dt = format->datatype;203 204 risp_write(isp, ISPCS_FILTER_ID_CH_REG(ch), BIT(vc));205 risp_write(isp, ISPCS_DT_CODE03_CH_REG(ch),206 ISPCS_DT_CODE03_EN3 | ISPCS_DT_CODE03_DT3(dt) |207 ISPCS_DT_CODE03_EN2 | ISPCS_DT_CODE03_DT2(dt) |208 ISPCS_DT_CODE03_EN1 | ISPCS_DT_CODE03_DT1(dt) |209 ISPCS_DT_CODE03_EN0 | ISPCS_DT_CODE03_DT0(dt));210 }211 212 /* Setup processing method. */213 risp_write(isp, ISPPROCMODE_DT_REG(format->datatype),214 ISPPROCMODE_DT_PROC_MODE_VC3(format->procmode) |215 ISPPROCMODE_DT_PROC_MODE_VC2(format->procmode) |216 ISPPROCMODE_DT_PROC_MODE_VC1(format->procmode) |217 ISPPROCMODE_DT_PROC_MODE_VC0(format->procmode));218 219 /* Start ISP. */220 risp_write(isp, ISPSTART_REG, ISPSTART_START);221 222 ret = v4l2_subdev_call(isp->remote, video, s_stream, 1);223 if (ret)224 risp_power_off(isp);225 226 return ret;227}228 229static void risp_stop(struct rcar_isp *isp)230{231 v4l2_subdev_call(isp->remote, video, s_stream, 0);232 233 /* Stop ISP. */234 risp_write(isp, ISPSTART_REG, ISPSTART_STOP);235 236 risp_power_off(isp);237}238 239static int risp_s_stream(struct v4l2_subdev *sd, int enable)240{241 struct rcar_isp *isp = sd_to_isp(sd);242 int ret = 0;243 244 mutex_lock(&isp->lock);245 246 if (!isp->remote) {247 ret = -ENODEV;248 goto out;249 }250 251 if (enable && isp->stream_count == 0) {252 ret = risp_start(isp);253 if (ret)254 goto out;255 } else if (!enable && isp->stream_count == 1) {256 risp_stop(isp);257 }258 259 isp->stream_count += enable ? 1 : -1;260out:261 mutex_unlock(&isp->lock);262 263 return ret;264}265 266static const struct v4l2_subdev_video_ops risp_video_ops = {267 .s_stream = risp_s_stream,268};269 270static int risp_set_pad_format(struct v4l2_subdev *sd,271 struct v4l2_subdev_state *sd_state,272 struct v4l2_subdev_format *format)273{274 struct rcar_isp *isp = sd_to_isp(sd);275 struct v4l2_mbus_framefmt *framefmt;276 277 mutex_lock(&isp->lock);278 279 if (!risp_code_to_fmt(format->format.code))280 format->format.code = rcar_isp_formats[0].code;281 282 if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {283 isp->mf = format->format;284 } else {285 framefmt = v4l2_subdev_state_get_format(sd_state, 0);286 *framefmt = format->format;287 }288 289 mutex_unlock(&isp->lock);290 291 return 0;292}293 294static int risp_get_pad_format(struct v4l2_subdev *sd,295 struct v4l2_subdev_state *sd_state,296 struct v4l2_subdev_format *format)297{298 struct rcar_isp *isp = sd_to_isp(sd);299 300 mutex_lock(&isp->lock);301 302 if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)303 format->format = isp->mf;304 else305 format->format = *v4l2_subdev_state_get_format(sd_state, 0);306 307 mutex_unlock(&isp->lock);308 309 return 0;310}311 312static const struct v4l2_subdev_pad_ops risp_pad_ops = {313 .set_fmt = risp_set_pad_format,314 .get_fmt = risp_get_pad_format,315 .link_validate = v4l2_subdev_link_validate_default,316};317 318static const struct v4l2_subdev_ops rcar_isp_subdev_ops = {319 .video = &risp_video_ops,320 .pad = &risp_pad_ops,321};322 323/* -----------------------------------------------------------------------------324 * Async handling and registration of subdevices and links325 */326 327static int risp_notify_bound(struct v4l2_async_notifier *notifier,328 struct v4l2_subdev *subdev,329 struct v4l2_async_connection *asd)330{331 struct rcar_isp *isp = notifier_to_isp(notifier);332 int pad;333 334 pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode,335 MEDIA_PAD_FL_SOURCE);336 if (pad < 0) {337 dev_err(isp->dev, "Failed to find pad for %s\n", subdev->name);338 return pad;339 }340 341 isp->remote = subdev;342 343 dev_dbg(isp->dev, "Bound %s pad: %d\n", subdev->name, pad);344 345 return media_create_pad_link(&subdev->entity, pad,346 &isp->subdev.entity, 0,347 MEDIA_LNK_FL_ENABLED |348 MEDIA_LNK_FL_IMMUTABLE);349}350 351static void risp_notify_unbind(struct v4l2_async_notifier *notifier,352 struct v4l2_subdev *subdev,353 struct v4l2_async_connection *asd)354{355 struct rcar_isp *isp = notifier_to_isp(notifier);356 357 isp->remote = NULL;358 359 dev_dbg(isp->dev, "Unbind %s\n", subdev->name);360}361 362static const struct v4l2_async_notifier_operations risp_notify_ops = {363 .bound = risp_notify_bound,364 .unbind = risp_notify_unbind,365};366 367static int risp_parse_dt(struct rcar_isp *isp)368{369 struct v4l2_async_connection *asd;370 struct fwnode_handle *fwnode;371 struct fwnode_handle *ep;372 unsigned int id;373 int ret;374 375 for (id = 0; id < 2; id++) {376 ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(isp->dev),377 0, id, 0);378 if (ep)379 break;380 }381 382 if (!ep) {383 dev_err(isp->dev, "Not connected to subdevice\n");384 return -EINVAL;385 }386 387 if (id == 1)388 isp->csi_input = RISP_CSI_INPUT1;389 390 fwnode = fwnode_graph_get_remote_endpoint(ep);391 fwnode_handle_put(ep);392 393 dev_dbg(isp->dev, "Found '%pOF'\n", to_of_node(fwnode));394 395 v4l2_async_subdev_nf_init(&isp->notifier, &isp->subdev);396 isp->notifier.ops = &risp_notify_ops;397 398 asd = v4l2_async_nf_add_fwnode(&isp->notifier, fwnode,399 struct v4l2_async_connection);400 fwnode_handle_put(fwnode);401 if (IS_ERR(asd))402 return PTR_ERR(asd);403 404 ret = v4l2_async_nf_register(&isp->notifier);405 if (ret)406 v4l2_async_nf_cleanup(&isp->notifier);407 408 return ret;409}410 411/* -----------------------------------------------------------------------------412 * Platform Device Driver413 */414 415static const struct media_entity_operations risp_entity_ops = {416 .link_validate = v4l2_subdev_link_validate,417};418 419static int risp_probe_resources(struct rcar_isp *isp,420 struct platform_device *pdev)421{422 isp->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);423 if (IS_ERR(isp->base))424 return PTR_ERR(isp->base);425 426 isp->rstc = devm_reset_control_get(&pdev->dev, NULL);427 428 return PTR_ERR_OR_ZERO(isp->rstc);429}430 431static const struct of_device_id risp_of_id_table[] = {432 { .compatible = "renesas,r8a779a0-isp" },433 { .compatible = "renesas,r8a779g0-isp" },434 { /* sentinel */ },435};436MODULE_DEVICE_TABLE(of, risp_of_id_table);437 438static int risp_probe(struct platform_device *pdev)439{440 struct rcar_isp *isp;441 unsigned int i;442 int ret;443 444 isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);445 if (!isp)446 return -ENOMEM;447 448 isp->dev = &pdev->dev;449 450 mutex_init(&isp->lock);451 452 ret = risp_probe_resources(isp, pdev);453 if (ret) {454 dev_err(isp->dev, "Failed to get resources\n");455 goto error_mutex;456 }457 458 platform_set_drvdata(pdev, isp);459 460 pm_runtime_enable(&pdev->dev);461 462 ret = risp_parse_dt(isp);463 if (ret)464 goto error_pm;465 466 isp->subdev.owner = THIS_MODULE;467 isp->subdev.dev = &pdev->dev;468 v4l2_subdev_init(&isp->subdev, &rcar_isp_subdev_ops);469 v4l2_set_subdevdata(&isp->subdev, &pdev->dev);470 snprintf(isp->subdev.name, sizeof(isp->subdev.name), "%s %s",471 KBUILD_MODNAME, dev_name(&pdev->dev));472 isp->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;473 474 isp->subdev.entity.function = MEDIA_ENT_F_VID_MUX;475 isp->subdev.entity.ops = &risp_entity_ops;476 477 isp->pads[RCAR_ISP_SINK].flags = MEDIA_PAD_FL_SINK;478 for (i = RCAR_ISP_PORT0; i < RCAR_ISP_NUM_PADS; i++)479 isp->pads[i].flags = MEDIA_PAD_FL_SOURCE;480 481 ret = media_entity_pads_init(&isp->subdev.entity, RCAR_ISP_NUM_PADS,482 isp->pads);483 if (ret)484 goto error_notifier;485 486 ret = v4l2_async_register_subdev(&isp->subdev);487 if (ret < 0)488 goto error_notifier;489 490 dev_info(isp->dev, "Using CSI-2 input: %u\n", isp->csi_input);491 492 return 0;493error_notifier:494 v4l2_async_nf_unregister(&isp->notifier);495 v4l2_async_nf_cleanup(&isp->notifier);496error_pm:497 pm_runtime_disable(&pdev->dev);498error_mutex:499 mutex_destroy(&isp->lock);500 501 return ret;502}503 504static void risp_remove(struct platform_device *pdev)505{506 struct rcar_isp *isp = platform_get_drvdata(pdev);507 508 v4l2_async_nf_unregister(&isp->notifier);509 v4l2_async_nf_cleanup(&isp->notifier);510 511 v4l2_async_unregister_subdev(&isp->subdev);512 513 pm_runtime_disable(&pdev->dev);514 515 mutex_destroy(&isp->lock);516}517 518static struct platform_driver rcar_isp_driver = {519 .driver = {520 .name = "rcar-isp",521 .suppress_bind_attrs = true,522 .of_match_table = risp_of_id_table,523 },524 .probe = risp_probe,525 .remove_new = risp_remove,526};527 528module_platform_driver(rcar_isp_driver);529 530MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>");531MODULE_DESCRIPTION("Renesas R-Car ISP Channel Selector driver");532MODULE_LICENSE("GPL");533