diff options
Diffstat (limited to 'drivers/media/pci')
72 files changed, 8480 insertions, 2339 deletions
diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index c631b8bbd386..8b9ca59f9434 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -3504,7 +3504,7 @@ static void bttv_remove(struct pci_dev *pci_dev) return; } -static int __maybe_unused bttv_suspend(struct device *dev) +static int bttv_suspend(struct device *dev) { struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); struct bttv *btv = to_bttv(v4l2_dev); @@ -3535,7 +3535,7 @@ static int __maybe_unused bttv_suspend(struct device *dev) return 0; } -static int __maybe_unused bttv_resume(struct device *dev) +static int bttv_resume(struct device *dev) { struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); struct bttv *btv = to_bttv(v4l2_dev); @@ -3573,7 +3573,7 @@ static const struct pci_device_id bttv_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, bttv_pci_tbl); -static SIMPLE_DEV_PM_OPS(bttv_pm_ops, +static DEFINE_SIMPLE_DEV_PM_OPS(bttv_pm_ops, bttv_suspend, bttv_resume); @@ -3582,7 +3582,7 @@ static struct pci_driver bttv_pci_driver = { .id_table = bttv_pci_tbl, .probe = bttv_probe, .remove = bttv_remove, - .driver.pm = &bttv_pm_ops, + .driver.pm = pm_sleep_ptr(&bttv_pm_ops), }; static int __init bttv_init_module(void) diff --git a/drivers/media/pci/cobalt/cobalt-driver.c b/drivers/media/pci/cobalt/cobalt-driver.c index 7b1ca1238c8d..ca96cbe4f194 100644 --- a/drivers/media/pci/cobalt/cobalt-driver.c +++ b/drivers/media/pci/cobalt/cobalt-driver.c @@ -524,8 +524,8 @@ static int cobalt_subdevs_init(struct cobalt *cobalt) &cobalt_edid); if (err) return err; - err = v4l2_subdev_call(s[i].sd, pad, set_fmt, NULL, - &sd_fmt); + err = v4l2_subdev_call(s[i].sd, pad, set_fmt, NULL, NULL, + &sd_fmt); if (err) return err; /* Reset channel video module */ @@ -608,8 +608,8 @@ static int cobalt_subdevs_hsma_init(struct cobalt *cobalt) if (err) return err; - err = v4l2_subdev_call(s->sd, pad, set_fmt, NULL, - &sd_fmt); + err = v4l2_subdev_call(s->sd, pad, set_fmt, NULL, NULL, + &sd_fmt); if (err) return err; cobalt->have_hsma_rx = true; diff --git a/drivers/media/pci/cobalt/cobalt-v4l2.c b/drivers/media/pci/cobalt/cobalt-v4l2.c index 51fd9576c6c2..ad89e62a2747 100644 --- a/drivers/media/pci/cobalt/cobalt-v4l2.c +++ b/drivers/media/pci/cobalt/cobalt-v4l2.c @@ -172,7 +172,7 @@ static void cobalt_enable_output(struct cobalt_stream *s) sd_fmt.format.code = MEDIA_BUS_FMT_RGB888_1X24; break; } - v4l2_subdev_call(s->sd, pad, set_fmt, NULL, &sd_fmt); + v4l2_subdev_call(s->sd, pad, set_fmt, NULL, NULL, &sd_fmt); iowrite32(0, &vo->control); /* 1080p60 */ @@ -223,14 +223,14 @@ static void cobalt_enable_input(struct cobalt_stream *s) iowrite32(M00235_CONTROL_BITMAP_ENABLE_MSK | (1 << M00235_CONTROL_BITMAP_PACK_FORMAT_OFST), &packer->control); - v4l2_subdev_call(s->sd, pad, set_fmt, NULL, + v4l2_subdev_call(s->sd, pad, set_fmt, NULL, NULL, &sd_fmt_yuyv); break; case V4L2_PIX_FMT_RGB24: iowrite32(M00235_CONTROL_BITMAP_ENABLE_MSK | (2 << M00235_CONTROL_BITMAP_PACK_FORMAT_OFST), &packer->control); - v4l2_subdev_call(s->sd, pad, set_fmt, NULL, + v4l2_subdev_call(s->sd, pad, set_fmt, NULL, NULL, &sd_fmt_rgb); break; case V4L2_PIX_FMT_BGR32: @@ -238,7 +238,7 @@ static void cobalt_enable_input(struct cobalt_stream *s) M00235_CONTROL_BITMAP_ENDIAN_FORMAT_MSK | (3 << M00235_CONTROL_BITMAP_PACK_FORMAT_OFST), &packer->control); - v4l2_subdev_call(s->sd, pad, set_fmt, NULL, + v4l2_subdev_call(s->sd, pad, set_fmt, NULL, NULL, &sd_fmt_rgb); break; } @@ -938,7 +938,7 @@ static int cobalt_s_fmt_vid_out(struct file *file, void *priv, s->ycbcr_enc = pix->ycbcr_enc; s->quantization = pix->quantization; v4l2_fill_mbus_format(&sd_fmt.format, pix, code); - v4l2_subdev_call(s->sd, pad, set_fmt, NULL, &sd_fmt); + v4l2_subdev_call(s->sd, pad, set_fmt, NULL, NULL, &sd_fmt); return 0; } diff --git a/drivers/media/pci/cx18/cx18-av-core.c b/drivers/media/pci/cx18/cx18-av-core.c index 4fb19d26ee29..f5961853eeae 100644 --- a/drivers/media/pci/cx18/cx18-av-core.c +++ b/drivers/media/pci/cx18/cx18-av-core.c @@ -930,6 +930,7 @@ static int cx18_av_s_ctrl(struct v4l2_ctrl *ctrl) } static int cx18_av_set_fmt(struct v4l2_subdev *sd, + const struct v4l2_subdev_client_info *ci, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { diff --git a/drivers/media/pci/cx18/cx18-controls.c b/drivers/media/pci/cx18/cx18-controls.c index 78eadad8b6e8..41f388ba22ab 100644 --- a/drivers/media/pci/cx18/cx18-controls.c +++ b/drivers/media/pci/cx18/cx18-controls.c @@ -85,7 +85,7 @@ static int cx18_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val) fmt->width = cxhdl->width / (is_mpeg1 ? 2 : 1); fmt->height = cxhdl->height; fmt->code = MEDIA_BUS_FMT_FIXED; - v4l2_subdev_call(cx->sd_av, pad, set_fmt, NULL, &format); + v4l2_subdev_call(cx->sd_av, pad, set_fmt, NULL, NULL, &format); return 0; } diff --git a/drivers/media/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c index 214fac7af61e..ac90ce6cd044 100644 --- a/drivers/media/pci/cx18/cx18-driver.c +++ b/drivers/media/pci/cx18/cx18-driver.c @@ -805,12 +805,12 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev, } if (dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32))) { CX18_ERR("No suitable DMA available, card %d\n", cx->instance); - return -EIO; + goto err_disable_device; } if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) { CX18_ERR("Cannot request encoder memory region, card %d\n", cx->instance); - return -EIO; + goto err_disable_device; } /* Enable bus mastering and memory mapped IO for the CX23418 */ @@ -834,6 +834,10 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev, cx->pci_dev->irq, pci_latency, (u64)cx->base_addr); return 0; + +err_disable_device: + pci_disable_device(pci_dev); + return -EIO; } static void cx18_init_subdevs(struct cx18 *cx) @@ -1120,6 +1124,7 @@ free_map: cx18_iounmap(cx); free_mem: release_mem_region(cx->base_addr, CX18_MEM_SIZE); + pci_disable_device(pci_dev); free_workqueues: destroy_workqueue(cx->in_work_queue); err: diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c index 0d676a57e24e..6f8d7f700016 100644 --- a/drivers/media/pci/cx18/cx18-ioctl.c +++ b/drivers/media/pci/cx18/cx18-ioctl.c @@ -150,7 +150,7 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh, format.format.width = cx->cxhdl.width = w; format.format.height = cx->cxhdl.height = h; format.format.code = MEDIA_BUS_FMT_FIXED; - v4l2_subdev_call(cx->sd_av, pad, set_fmt, NULL, &format); + v4l2_subdev_call(cx->sd_av, pad, set_fmt, NULL, NULL, &format); return cx18_g_fmt_vid_cap(file, fh, fmt); } diff --git a/drivers/media/pci/cx18/cx18-queue.c b/drivers/media/pci/cx18/cx18-queue.c index 04d6828f0259..a973367d4cde 100644 --- a/drivers/media/pci/cx18/cx18-queue.c +++ b/drivers/media/pci/cx18/cx18-queue.c @@ -114,7 +114,10 @@ static inline void cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s, if (list_is_singular(&mdl->buf_list)) { buf = list_first_entry(&mdl->buf_list, struct cx18_buffer, list); - buf->bytesused = mdl->bytesused; + if (mdl->bytesused > s->buf_size) + buf->bytesused = s->buf_size; + else + buf->bytesused = mdl->bytesused; buf->readpos = 0; cx18_buf_sync_for_cpu(s, buf); } else { diff --git a/drivers/media/pci/cx18/cx23418.h b/drivers/media/pci/cx18/cx23418.h index 22486f39bcda..4ee45be757cb 100644 --- a/drivers/media/pci/cx18/cx23418.h +++ b/drivers/media/pci/cx18/cx23418.h @@ -24,7 +24,7 @@ #define CX18_CREATE_TASK (MGR_CMD_MASK | 0x0001) /* Description: This command destroys an instance of a task - IN[0] - Task handle. Hanlde of the task to destroy + IN[0] - Task handle. Handle of the task to destroy ReturnCode - One of the ERR_SYS_... */ #define CX18_DESTROY_TASK (MGR_CMD_MASK | 0x0002) diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 5fb26285e4af..7498091176b7 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -2246,6 +2246,10 @@ static void cx23885_finidev(struct pci_dev *pci_dev) /* unregister stuff */ free_irq(pci_dev->irq, dev); + cancel_work_sync(&dev->cx25840_work); + cancel_work_sync(&dev->ir_rx_work); + cancel_work_sync(&dev->ir_tx_work); + pci_disable_device(pci_dev); cx23885_dev_unregister(dev); diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index f240ccda40ed..348abde0ba26 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -1158,8 +1158,12 @@ static int dvb_register_ci_mac(struct cx23885_tsport *port) info.platform_data = &sp2_config; request_module(info.type); client_ci = i2c_new_client_device(&i2c_bus->i2c_adap, &info); - if (!i2c_client_has_driver(client_ci)) + if (IS_ERR(client_ci)) + return PTR_ERR(client_ci); + if (!client_ci->dev.driver) { + i2c_unregister_device(client_ci); return -ENODEV; + } if (!try_module_get(client_ci->dev.driver->owner)) { i2c_unregister_device(client_ci); return -ENODEV; @@ -1202,7 +1206,7 @@ static int dvb_register(struct cx23885_tsport *port) int (*p_set_voltage)(struct dvb_frontend *fe, enum fe_sec_voltage voltage) = NULL; int mfe_shared = 0; /* bus not shared by default */ - int ret; + int ret = -EINVAL; /* Get the first frontend */ fe0 = vb2_dvb_get_frontend(&port->frontends, 1); @@ -2586,8 +2590,10 @@ static int dvb_register(struct cx23885_tsport *port) goto frontend_detach; ret = dvb_register_ci_mac(port); - if (ret) + if (ret) { + vb2_dvb_unregister_bus(&port->frontends); goto frontend_detach; + } return 0; @@ -2618,7 +2624,7 @@ frontend_detach: port->gate_ctrl = NULL; vb2_dvb_dealloc_frontends(&port->frontends); - return -EINVAL; + return ret; } int cx23885_dvb_register(struct cx23885_tsport *port) diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 14d219fd1d8a..94ce00154e1f 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -134,7 +134,7 @@ int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm) format.format.width = dev->width; format.format.height = dev->height; format.format.field = dev->field; - call_all(dev, pad, set_fmt, NULL, &format); + call_all(dev, pad, set_fmt, NULL, NULL, &format); return 0; } @@ -619,7 +619,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, dev->width, dev->height, dev->field); v4l2_fill_mbus_format(&format.format, &f->fmt.pix, MEDIA_BUS_FMT_FIXED); - call_all(dev, pad, set_fmt, NULL, &format); + call_all(dev, pad, set_fmt, NULL, NULL, &format); v4l2_fill_pix_format(&f->fmt.pix, &format.format); /* set_fmt overwrites f->fmt.pix.field, restore it */ f->fmt.pix.field = dev->field; diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c index 5d9ce4f9af01..c96d289e5ab7 100644 --- a/drivers/media/pci/cx88/cx88-input.c +++ b/drivers/media/pci/cx88/cx88-input.c @@ -259,7 +259,7 @@ static void cx88_ir_close(struct rc_dev *rc) int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) { struct cx88_IR *ir; - struct rc_dev *dev; + struct rc_dev *dev = NULL; char *ir_codes = NULL; u64 rc_proto = RC_PROTO_BIT_OTHER; int err = -ENOMEM; @@ -268,11 +268,8 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) */ ir = kzalloc_obj(*ir); - dev = rc_allocate_device(RC_DRIVER_IR_RAW); - if (!ir || !dev) - goto err_out_free; - - ir->dev = dev; + if (!ir) + return -ENOMEM; /* detect & configure */ switch (core->boardnr) { @@ -439,6 +436,13 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) goto err_out_free; } + dev = rc_allocate_device(ir->sampling ? + RC_DRIVER_IR_RAW : RC_DRIVER_SCANCODE); + if (!dev) + goto err_out_free; + + ir->dev = dev; + /* * The usage of mask_keycode were very convenient, due to several * reasons. Among others, the scancode tables were using the scancode @@ -477,12 +481,10 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) dev->close = cx88_ir_close; dev->scancode_mask = hardware_mask; - if (ir->sampling) { + if (ir->sampling) dev->timeout = MS_TO_US(10); /* 10 ms */ - } else { - dev->driver_type = RC_DRIVER_SCANCODE; + else dev->allowed_protocols = rc_proto; - } ir->core = core; core->ir = ir; diff --git a/drivers/media/pci/cx88/cx88-mpeg.c b/drivers/media/pci/cx88/cx88-mpeg.c index 0c07ad335799..cd2366768f4a 100644 --- a/drivers/media/pci/cx88/cx88-mpeg.c +++ b/drivers/media/pci/cx88/cx88-mpeg.c @@ -392,7 +392,8 @@ static int cx8802_init_common(struct cx8802_dev *dev) err = dma_set_mask(&dev->pci->dev, DMA_BIT_MASK(32)); if (err) { pr_err("Oops: no 32bit PCI DMA ???\n"); - return -EIO; + err = -EIO; + goto fail_disable_device; } dev->pci_rev = dev->pci->revision; @@ -413,13 +414,17 @@ static int cx8802_init_common(struct cx8802_dev *dev) IRQF_SHARED, dev->core->name, dev); if (err < 0) { pr_err("can't get IRQ %d\n", dev->pci->irq); - return err; + goto fail_disable_device; } cx_set(MO_PCI_INTMSK, core->pci_irqmask); /* everything worked */ pci_set_drvdata(dev->pci, dev); return 0; + +fail_disable_device: + pci_disable_device(dev->pci); + return err; } static void cx8802_fini_common(struct cx8802_dev *dev) diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index eaa46a2f92e7..f8f2843495f3 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -1549,7 +1549,7 @@ static void cx8800_finidev(struct pci_dev *pci_dev) kfree(dev); } -static int __maybe_unused cx8800_suspend(struct device *dev_d) +static int cx8800_suspend(struct device *dev_d) { struct cx8800_dev *dev = dev_get_drvdata(dev_d); struct cx88_core *core = dev->core; @@ -1576,7 +1576,7 @@ static int __maybe_unused cx8800_suspend(struct device *dev_d) return 0; } -static int __maybe_unused cx8800_resume(struct device *dev_d) +static int cx8800_resume(struct device *dev_d) { struct cx8800_dev *dev = dev_get_drvdata(dev_d); struct cx88_core *core = dev->core; @@ -1617,14 +1617,14 @@ static const struct pci_device_id cx8800_pci_tbl[] = { }; MODULE_DEVICE_TABLE(pci, cx8800_pci_tbl); -static SIMPLE_DEV_PM_OPS(cx8800_pm_ops, cx8800_suspend, cx8800_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(cx8800_pm_ops, cx8800_suspend, cx8800_resume); static struct pci_driver cx8800_pci_driver = { .name = "cx8800", .id_table = cx8800_pci_tbl, .probe = cx8800_initdev, .remove = cx8800_finidev, - .driver.pm = &cx8800_pm_ops, + .driver.pm = pm_sleep_ptr(&cx8800_pm_ops), }; module_pci_driver(cx8800_pci_driver); diff --git a/drivers/media/pci/hws/hws.h b/drivers/media/pci/hws/hws.h index 8fbe1fe27844..d87d52674b69 100644 --- a/drivers/media/pci/hws/hws.h +++ b/drivers/media/pci/hws/hws.h @@ -33,8 +33,8 @@ struct hws_pix_state { u32 sizeimage; /* full frame */ enum v4l2_field field; /* V4L2_FIELD_NONE or INTERLACED */ enum v4l2_colorspace colorspace; /* e.g., REC709 */ - enum v4l2_ycbcr_encoding ycbcr_enc; /* V4L2_YCBCR_ENC_DEFAULT */ - enum v4l2_quantization quantization; /* V4L2_QUANTIZATION_LIM_RANGE */ + enum v4l2_ycbcr_encoding ycbcr_enc; /* V4L2_YCBCR_ENC_601 */ + enum v4l2_quantization quantization; /* V4L2_QUANTIZATION_FULL_RANGE */ enum v4l2_xfer_func xfer_func; /* V4L2_XFER_FUNC_DEFAULT */ bool interlaced; /* cached hardware state */ u32 half_size; /* hardware half-frame size */ diff --git a/drivers/media/pci/hws/hws_pci.c b/drivers/media/pci/hws/hws_pci.c index 30bb7d34465b..f06e60dc2ee6 100644 --- a/drivers/media/pci/hws/hws_pci.c +++ b/drivers/media/pci/hws/hws_pci.c @@ -33,8 +33,8 @@ static unsigned long long hws_elapsed_us(u64 start_ns) } /* register layout inside HWS_REG_DEVICE_INFO */ -#define DEVINFO_VER GENMASK(7, 0) -#define DEVINFO_SUBVER GENMASK(15, 8) +#define DEVINFO_VER GENMASK(15, 8) +#define DEVINFO_SUBVER GENMASK(23, 16) #define DEVINFO_YV12 GENMASK(31, 28) #define DEVINFO_HWKEY GENMASK(27, 24) #define DEVINFO_PORTID GENMASK(25, 24) /* low 2 bits of HW-key */ @@ -441,7 +441,7 @@ static int hws_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) hws_init_video_sys(hws, false); /* 5) Init channels (video state, locks, vb2, ctrls) */ - for (i = 0; i < hws->max_channels; i++) { + for (i = 0; i < hws->cur_max_video_ch; i++) { ret = hws_video_init_channel(hws, i); if (ret) { dev_err(&pdev->dev, "video channel init failed (ch=%d)\n", i); diff --git a/drivers/media/pci/hws/hws_reg.h b/drivers/media/pci/hws/hws_reg.h index e4fb4af44434..ba1d22f4376c 100644 --- a/drivers/media/pci/hws/hws_reg.h +++ b/drivers/media/pci/hws/hws_reg.h @@ -70,7 +70,8 @@ #define HWS_CTL_IRQ_ENABLE_BIT BIT(0) /* Global interrupt enable bit */ /* Write 0x00 to fully reset decoder, * set bit 31=1 to "start run", - * low byte=0x13 selects YUYV/BT.709/etc, + * low byte=0x13 is the vendor baseline capture-mode value; its individual + * bit meanings are not documented, * in ReadChipId() we also write 0x00 and 0x10 here for chip-ID sequencing. */ @@ -121,9 +122,10 @@ #define HWS_REG_DEVICE_INFO (CVBS_IN_BASE + 88 * PCIE_BARADDROFSIZE) /* * Reading this 32-bit word returns: - * bits 7:0 = "device version" - * bits 15:8 = "device sub-version" - * bits 23:24 = "HW key / port ID" etc. + * bits 7:0 = unused by the baseline driver + * bits 15:8 = device version + * bits 23:16 = device sub-version + * bits 27:24 = HW key (port ID in bits 25:24) * bits 31:28 = "support YV12" flags */ diff --git a/drivers/media/pci/hws/hws_v4l2_ioctl.c b/drivers/media/pci/hws/hws_v4l2_ioctl.c index 0303e311ee4a..ce396b7225d2 100644 --- a/drivers/media/pci/hws/hws_v4l2_ioctl.c +++ b/drivers/media/pci/hws/hws_v4l2_ioctl.c @@ -511,8 +511,9 @@ static inline void hws_set_colorimetry_state(struct hws_pix_state *p) { bool sd = p->height <= 576; + /* Captured samples use full-range BT.601 Y'CbCr at every resolution. */ p->colorspace = sd ? V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709; - p->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + p->ycbcr_enc = V4L2_YCBCR_ENC_601; p->quantization = V4L2_QUANTIZATION_FULL_RANGE; p->xfer_func = V4L2_XFER_FUNC_DEFAULT; } @@ -736,7 +737,7 @@ static inline void hws_set_colorimetry_fmt(struct v4l2_pix_format *p) bool sd = p->height <= 576; p->colorspace = sd ? V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709; - p->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + p->ycbcr_enc = V4L2_YCBCR_ENC_601; p->quantization = V4L2_QUANTIZATION_FULL_RANGE; p->xfer_func = V4L2_XFER_FUNC_DEFAULT; } diff --git a/drivers/media/pci/hws/hws_video.c b/drivers/media/pci/hws/hws_video.c index 18e4bc6901d3..dbe0fc2a66b5 100644 --- a/drivers/media/pci/hws/hws_video.c +++ b/drivers/media/pci/hws/hws_video.c @@ -327,7 +327,7 @@ int hws_video_init_channel(struct hws_pcie_dev *pdev, int ch) vid->pix.sizeimage = vid->pix.bytesperline * vid->pix.height; vid->pix.field = V4L2_FIELD_NONE; vid->pix.colorspace = V4L2_COLORSPACE_REC709; - vid->pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + vid->pix.ycbcr_enc = V4L2_YCBCR_ENC_601; vid->pix.quantization = V4L2_QUANTIZATION_FULL_RANGE; vid->pix.xfer_func = V4L2_XFER_FUNC_DEFAULT; vid->pix.interlaced = false; @@ -895,6 +895,8 @@ static void hws_video_apply_mode_change(struct hws_pcie_dev *pdx, v->pix.width = w; v->pix.height = h; v->pix.interlaced = interlaced; + v->pix.colorspace = h <= 576 ? V4L2_COLORSPACE_SMPTE170M : + V4L2_COLORSPACE_REC709; hws_set_current_dv_timings(v, w, h, interlaced); v->current_fps = fps; @@ -1292,6 +1294,8 @@ static void hws_stop_streaming(struct vb2_queue *q) WRITE_ONCE(v->stop_requested, true); hws_enable_video_capture(v->parent, v->channel_index, false); + if (hws->irq >= 0) + synchronize_irq(hws->irq); /* 2) Collect in-flight + queued under the IRQ lock */ spin_lock_irqsave(&v->irq_lock, flags); diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index bd64c0400c0d..e476e269a50a 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -8,6 +8,7 @@ #include <linux/dmi.h> #include <linux/i2c.h> #include <linux/mei_cl_bus.h> +#include <linux/pci.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/property.h> @@ -15,6 +16,7 @@ #include <linux/workqueue.h> #include <media/ipu-bridge.h> +#include <media/ipu6-pci-table.h> #include <media/v4l2-fwnode.h> #define ADEV_DEV(adev) ACPI_PTR(&((adev)->dev)) @@ -48,6 +50,14 @@ * * Please keep the list sorted by ACPI HID. */ +/* IPU6 variants whose CSI-2 receiver needs the ov5693 clock lane gated */ +static const u16 ipu6_ov5693_ncont_clk[] = { + PCI_DEVICE_ID_INTEL_IPU6, /* Tiger Lake */ + PCI_DEVICE_ID_INTEL_IPU6EP_ADLP, /* Alder Lake-P */ + PCI_DEVICE_ID_INTEL_IPU6EP_ADLN, /* Alder Lake-N */ + 0 +}; + static const struct ipu_sensor_config ipu_supported_sensors[] = { /* Himax HM1092 */ IPU_SENSOR_CONFIG("HIMX1092", 2, 180000000, 180480000), @@ -60,6 +70,9 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { /* GalaxyCore GC0310 */ IPU_SENSOR_CONFIG("INT0310", 1, 55692000), /* Omnivision OV5693 */ + IPU_SENSOR_CONFIG_MATCH_FL("INT33BE", ipu6_ov5693_ncont_clk, + IPU_BR_FL_CSI2_CLK_NONCONTINUOUS, + 1, 419200000), IPU_SENSOR_CONFIG("INT33BE", 1, 419200000), /* Onsemi MT9M114 */ IPU_SENSOR_CONFIG("INT33F0", 1, 384000000), @@ -75,19 +88,22 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { IPU_SENSOR_CONFIG("INT3537", 1, 437000000), /* Lontium lt6911uxe */ IPU_SENSOR_CONFIG("INTC10C5", 0), - /* Omnivision OV01A10 / OV01A1S */ + /* Omnivision OV01A10 / OV01A1B / OV01A1S */ IPU_SENSOR_CONFIG("OVTI01A0", 1, 400000000), + IPU_SENSOR_CONFIG("OVTI01AB", 1, 400000000), IPU_SENSOR_CONFIG("OVTI01AS", 1, 400000000), /* Omnivision OV02C10 */ IPU_SENSOR_CONFIG("OVTI02C1", 1, 400000000), /* Omnivision OV02E10 */ IPU_SENSOR_CONFIG("OVTI02E1", 1, 360000000), /* Omnivision ov05c10 */ - IPU_SENSOR_CONFIG("OVTI05C1", 1, 480000000), + IPU_SENSOR_CONFIG("OVTI05C1", 2, 480000000, 900000000), /* Omnivision OV08A10 */ IPU_SENSOR_CONFIG("OVTI08A1", 1, 500000000), /* Omnivision OV08x40 */ IPU_SENSOR_CONFIG("OVTI08F4", 3, 400000000, 749000000, 800000000), + /* Omnivision OV13858 */ + IPU_SENSOR_CONFIG("OVTID858", 2, 540000000, 270000000), /* Omnivision OV13B10 */ IPU_SENSOR_CONFIG("OVTI13B1", 1, 560000000), IPU_SENSOR_CONFIG("OVTIDB10", 1, 560000000), @@ -95,6 +111,11 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { IPU_SENSOR_CONFIG("OVTI2680", 1, 331200000), /* Omnivision OV5675 */ IPU_SENSOR_CONFIG("OVTI5675", 1, 450000000), + /* Omnivision OV5693 */ + IPU_SENSOR_CONFIG_MATCH_FL("OVTI5693", ipu6_ov5693_ncont_clk, + IPU_BR_FL_CSI2_CLK_NONCONTINUOUS, + 1, 419200000), + IPU_SENSOR_CONFIG("OVTI5693", 1, 419200000), /* Omnivision OV8856 */ IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000), /* Sony IMX471 */ @@ -127,6 +148,13 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { { .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 14 (Dell 14 Premium) DA14250"), + }, + .driver_data = "OVTI02C1", + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 14 9440"), }, .driver_data = "OVTI02C1", @@ -145,6 +173,13 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { }, .driver_data = "OVTI02C1", }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Dell Pro 14 Premium PA14260"), + }, + .driver_data = "OVTI08F4", + }, /* * The first four characters of DMI_BOARD_NAME identify the Lenovo * machine type/model. For example, a DMI_BOARD_NAME starting with @@ -185,6 +220,14 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { .driver_data = "SONY471A", }, { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, + "Surface Pro for Business 11th Edition with Intel"), + }, + .driver_data = "OVTID858", + }, + { /* Samsung Galaxy Book5 Pro 360 */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), @@ -192,6 +235,14 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { }, .driver_data = "OVTI02E1", }, + { + /* Samsung Galaxy Book3 Ultra */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "960XFH"), + }, + .driver_data = "OVTI02C1", + }, {} /* Terminating entry */ }; @@ -326,8 +377,8 @@ static int ipu_bridge_check_ivsc_dev(struct ipu_sensor *sensor, if (adev) { csi_dev = ipu_bridge_get_ivsc_csi_dev(adev); if (!csi_dev) { - acpi_dev_put(adev); dev_err(ADEV_DEV(adev), "Failed to find MEI or CVS CSI dev\n"); + acpi_dev_put(adev); return -ENODEV; } @@ -436,6 +487,7 @@ static enum v4l2_fwnode_orientation ipu_bridge_parse_orientation(struct acpi_dev int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor) { + acpi_handle handle = acpi_device_handle(ACPI_PTR(adev)); struct ipu_sensor_ssdb ssdb = {}; int ret; @@ -459,8 +511,15 @@ int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor) sensor->rotation = ipu_bridge_parse_rotation(adev, &ssdb); sensor->orientation = ipu_bridge_parse_orientation(adev); - if (ssdb.vcmtype) + acpi_handle_debug(handle, + "CSI-2 port %u, lanes %u, mclkspeed %u, rotation %u (SSDB %u), orientation %u\n", + sensor->link, sensor->lanes, sensor->mclkspeed, + sensor->rotation, ssdb.degree, sensor->orientation); + + if (ssdb.vcmtype) { sensor->vcm_type = ipu_vcm_types[ssdb.vcmtype - 1]; + acpi_handle_debug(handle, "VCM %s\n", sensor->vcm_type); + } return 0; } @@ -473,6 +532,7 @@ static void ipu_bridge_create_fwnode_properties( { struct ipu_property_names *names = &sensor->prop_names; struct software_node *nodes = sensor->swnodes; + unsigned int i = 0; sensor->prop_names = prop_names; @@ -530,21 +590,25 @@ static void ipu_bridge_create_fwnode_properties( PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref); } - sensor->ep_properties[0] = PROPERTY_ENTRY_U32( - sensor->prop_names.bus_type, - V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); - sensor->ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN( - sensor->prop_names.data_lanes, - bridge->data_lanes, sensor->lanes); - sensor->ep_properties[2] = PROPERTY_ENTRY_REF_ARRAY( - sensor->prop_names.remote_endpoint, - sensor->local_ref); + sensor->ep_properties[IPU_BRIDGE_NEXT_PROPERTY(i, EP_BUS_TYPE)] = + PROPERTY_ENTRY_U32(names->bus_type, + V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); + sensor->ep_properties[IPU_BRIDGE_NEXT_PROPERTY(i, EP_DATA_LANES)] = + PROPERTY_ENTRY_U32_ARRAY_LEN(names->data_lanes, + bridge->data_lanes, sensor->lanes); + sensor->ep_properties[IPU_BRIDGE_NEXT_PROPERTY(i, EP_REMOTE_EP)] = + PROPERTY_ENTRY_REF_ARRAY(names->remote_endpoint, + sensor->local_ref); if (cfg->nr_link_freqs > 0) - sensor->ep_properties[3] = PROPERTY_ENTRY_U64_ARRAY_LEN( - sensor->prop_names.link_frequencies, - cfg->link_freqs, - cfg->nr_link_freqs); + sensor->ep_properties[IPU_BRIDGE_NEXT_PROPERTY(i, EP_LINK_FREQUENCIES)] = + PROPERTY_ENTRY_U64_ARRAY_LEN(names->link_frequencies, + cfg->link_freqs, + cfg->nr_link_freqs); + + if (cfg->flags & IPU_BR_FL_CSI2_CLK_NONCONTINUOUS) + sensor->ep_properties[IPU_BRIDGE_NEXT_PROPERTY(i, EP_CLOCK_NONCONTINUOUS)] = + PROPERTY_ENTRY_BOOL("clock-noncontinuous"); sensor->ipu_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN( sensor->prop_names.data_lanes, @@ -856,8 +920,8 @@ static int ipu_bridge_connect_sensor(const struct ipu_sensor_config *cfg, if (ret) goto err_free_swnodes; - dev_info(bridge->dev, "Found supported sensor %s\n", - acpi_dev_name(adev)); + dev_info(bridge->dev, "Found supported sensor %s (%pfw)\n", + acpi_dev_name(adev), primary); bridge->n_sensors++; } @@ -874,8 +938,28 @@ err_put_adev: return ret; } +/* + * Whether a sensor config applies to the IPU this bridge sits on. A config + * listing PCI product IDs only applies to those IPUs. + */ +static bool ipu_bridge_config_matches(const struct ipu_sensor_config *cfg, + struct ipu_bridge *bridge) +{ + const u16 *id; + + if (!cfg->pci_ids) + return true; + + for (id = cfg->pci_ids; *id; id++) + if (*id == bridge->pci_id) + return true; + + return false; +} + static int ipu_bridge_connect_sensors(struct ipu_bridge *bridge) { + const char *done_hid = NULL; unsigned int i; int ret; @@ -883,9 +967,22 @@ static int ipu_bridge_connect_sensors(struct ipu_bridge *bridge) const struct ipu_sensor_config *cfg = &ipu_supported_sensors[i]; + /* + * Entries for one HID are adjacent, IPU-specific ones first, + * so the generic entry is skipped once a specific one has + * matched and the sensor is not connected twice. + */ + if (done_hid && !strcmp(cfg->hid, done_hid)) + continue; + + if (!ipu_bridge_config_matches(cfg, bridge)) + continue; + ret = ipu_bridge_connect_sensor(cfg, bridge); if (ret) goto err_unregister_sensors; + + done_hid = cfg->hid; } return 0; @@ -942,6 +1039,18 @@ static int ipu_bridge_check_fwnode_graph(struct fwnode_handle *fwnode) return ipu_bridge_check_fwnode_graph(fwnode->secondary); } +struct pci_dev *ipu_bridge_get_ipu6(void) +{ + struct pci_dev *ipu = NULL; + + for (unsigned int i = 0; !ipu && ipu6_pci_tbl[i].vendor; i++) + ipu = pci_get_device(ipu6_pci_tbl[i].vendor, + ipu6_pci_tbl[i].device, NULL); + + return ipu; +} +EXPORT_SYMBOL_NS_GPL(ipu_bridge_get_ipu6, "INTEL_IPU_BRIDGE"); + static DEFINE_MUTEX(ipu_bridge_mutex); int ipu_bridge_init(struct device *dev, @@ -969,6 +1078,7 @@ int ipu_bridge_init(struct device *dev, sizeof(bridge->ipu_node_name)); bridge->ipu_hid_node.name = bridge->ipu_node_name; bridge->dev = dev; + bridge->pci_id = dev_is_pci(dev) ? to_pci_dev(dev)->device : 0; bridge->parse_sensor_fwnode = parse_sensor_fwnode; ret = software_node_register(&bridge->ipu_hid_node); diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index eb1824ee86fd..4d2b231291f5 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -1229,6 +1229,7 @@ static int cio2_subdev_init_state(struct v4l2_subdev *sd, } static int cio2_subdev_set_fmt(struct v4l2_subdev *sd, + const struct v4l2_subdev_client_info *ci, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { diff --git a/drivers/media/pci/intel/ipu6/Kconfig b/drivers/media/pci/intel/ipu6/Kconfig index 1129e2beb4be..a1d036a78b57 100644 --- a/drivers/media/pci/intel/ipu6/Kconfig +++ b/drivers/media/pci/intel/ipu6/Kconfig @@ -16,3 +16,12 @@ config VIDEO_INTEL_IPU6 To compile this driver, say Y here! It contains 2 modules - intel_ipu6 and intel_ipu6_isys. + +config VIDEO_INTEL_IPU6_IPU7 + bool "Support IPU 7 and IPU 7.5 in ipu6 driver by default" + depends on VIDEO_INTEL_IPU6 + default y + help + Support Intel IPU 7 and IPU 7.5 in ipu6 driver by default, instead of + the ipu7 staging driver. This behaviour can be changed at runtime with + module parameters in the respective drivers. diff --git a/drivers/media/pci/intel/ipu6/Makefile b/drivers/media/pci/intel/ipu6/Makefile index a821b0a1567f..676bc978b972 100644 --- a/drivers/media/pci/intel/ipu6/Makefile +++ b/drivers/media/pci/intel/ipu6/Makefile @@ -4,20 +4,26 @@ intel-ipu6-y := ipu6.o \ ipu6-bus.o \ ipu6-dma.o \ ipu6-mmu.o \ + ipu6-mmu-hw.o \ + ipu7-mmu-hw.o \ ipu6-buttress.o \ ipu6-cpd.o \ - ipu6-fw-com.o + ipu6-fw-com.o \ + ipu7-fw-com.o \ + ipu7-boot.o obj-$(CONFIG_VIDEO_INTEL_IPU6) += intel-ipu6.o intel-ipu6-isys-y := ipu6-isys.o \ ipu6-isys-csi2.o \ ipu6-fw-isys.o \ + ipu7-fw-isys.o \ ipu6-isys-video.o \ ipu6-isys-queue.o \ ipu6-isys-subdev.o \ ipu6-isys-mcd-phy.o \ ipu6-isys-jsl-phy.o \ - ipu6-isys-dwc-phy.o + ipu6-isys-dwc-phy.o \ + ipu7-isys-csi-phy.o obj-$(CONFIG_VIDEO_INTEL_IPU6) += intel-ipu6-isys.o diff --git a/drivers/media/pci/intel/ipu6/ipu6-bus.h b/drivers/media/pci/intel/ipu6/ipu6-bus.h index a08c5468d536..d2f93eb03dea 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-bus.h +++ b/drivers/media/pci/intel/ipu6/ipu6-bus.h @@ -26,17 +26,18 @@ struct ipu6_bus_device { struct ipu6_mmu *mmu; struct ipu6_device *isp; const struct ipu6_buttress_ctrl *ctrl; - const struct firmware *fw; struct sg_table fw_sgt; u64 *pkg_dir; dma_addr_t pkg_dir_dma_addr; unsigned int pkg_dir_size; + u32 fw_entry; }; struct ipu6_auxdrv_data { irqreturn_t (*isr)(struct ipu6_bus_device *adev); irqreturn_t (*isr_threaded)(struct ipu6_bus_device *adev); bool wake_isr_thread; + const struct ipu6_fw_isys_ops *fw_ops; }; #define to_ipu6_bus_device(_dev) \ @@ -45,6 +46,9 @@ struct ipu6_auxdrv_data { container_of(_auxdev, struct ipu6_bus_device, auxdev) #define ipu6_bus_get_drvdata(adev) dev_get_drvdata(&(adev)->auxdev.dev) +extern const struct ipu6_fw_isys_ops ipu6_fw_isys_ops; +extern const struct ipu6_fw_isys_ops ipu7_fw_isys_ops; + struct ipu6_bus_device * ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent, void *pdata, const struct ipu6_buttress_ctrl *ctrl, diff --git a/drivers/media/pci/intel/ipu6/ipu6-buttress.c b/drivers/media/pci/intel/ipu6/ipu6-buttress.c index e0ecb4c8b081..105de1744dff 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-buttress.c +++ b/drivers/media/pci/intel/ipu6/ipu6-buttress.c @@ -3,6 +3,7 @@ * Copyright (C) 2013--2024 Intel Corporation */ +#include <asm/cpu_device_id.h> #include <linux/bitfield.h> #include <linux/bits.h> #include <linux/completion.h> @@ -55,16 +56,11 @@ #define BUTTRESS_MAX_CONSECUTIVE_IRQS 100 -static const u32 ipu6_adev_irq_mask[2] = { - BUTTRESS_ISR_IS_IRQ, - BUTTRESS_ISR_PS_IRQ -}; - -int ipu6_buttress_ipc_reset(struct ipu6_device *isp, - struct ipu6_buttress_ipc *ipc) +int ipu6_buttress_ipc_reset(struct ipu6_device *isp) { unsigned int retries = BUTTRESS_IPC_RESET_RETRY; struct ipu6_buttress *b = &isp->buttress; + const struct ipu6_buttress_registers *regs = b->regs; u32 val = 0, csr_in_clr; if (!isp->secure_mode) { @@ -75,11 +71,11 @@ int ipu6_buttress_ipc_reset(struct ipu6_device *isp, mutex_lock(&b->ipc_mutex); /* Clear-by-1 CSR (all bits), corresponding internal states. */ - val = readl(isp->base + ipc->csr_in); - writel(val, isp->base + ipc->csr_in); + val = readl(isp->base + regs->csr_in); + writel(val, isp->base + regs->csr_in); /* Set peer CSR bit IPC_PEER_COMP_ACTIONS_RST_PHASE1 */ - writel(ENTRY, isp->base + ipc->csr_out); + writel(ENTRY, isp->base + regs->csr_out); /* * Clear-by-1 all CSR bits EXCEPT following * bits: @@ -94,7 +90,7 @@ int ipu6_buttress_ipc_reset(struct ipu6_device *isp, do { usleep_range(400, 500); - val = readl(isp->base + ipc->csr_in); + val = readl(isp->base + regs->csr_in); switch (val) { case ENTRY | EXIT: case ENTRY | EXIT | QUERY: @@ -105,8 +101,8 @@ int ipu6_buttress_ipc_reset(struct ipu6_device *isp, * 2) Set peer CSR bit * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE. */ - writel(ENTRY | EXIT, isp->base + ipc->csr_in); - writel(QUERY, isp->base + ipc->csr_out); + writel(ENTRY | EXIT, isp->base + regs->csr_in); + writel(QUERY, isp->base + regs->csr_out); break; case ENTRY: case ENTRY | QUERY: @@ -117,8 +113,8 @@ int ipu6_buttress_ipc_reset(struct ipu6_device *isp, * 2) Set peer CSR bit * IPC_PEER_COMP_ACTIONS_RST_PHASE1. */ - writel(ENTRY | QUERY, isp->base + ipc->csr_in); - writel(ENTRY, isp->base + ipc->csr_out); + writel(ENTRY | QUERY, isp->base + regs->csr_in); + writel(ENTRY, isp->base + regs->csr_out); break; case EXIT: case EXIT | QUERY: @@ -135,17 +131,17 @@ int ipu6_buttress_ipc_reset(struct ipu6_device *isp, * 3) Set peer CSR bit * IPC_PEER_COMP_ACTIONS_RST_PHASE2. */ - writel(EXIT, isp->base + ipc->csr_in); - writel(0, isp->base + ipc->db0_in); - writel(csr_in_clr, isp->base + ipc->csr_in); - writel(EXIT, isp->base + ipc->csr_out); + writel(EXIT, isp->base + regs->csr_in); + writel(0, isp->base + regs->db0_in); + writel(csr_in_clr, isp->base + regs->csr_in); + writel(EXIT, isp->base + regs->csr_out); /* * Read csr_in again to make sure if RST_PHASE2 is done. * If csr_in is QUERY, it should be handled again. */ usleep_range(200, 300); - val = readl(isp->base + ipc->csr_in); + val = readl(isp->base + regs->csr_in); if (val & QUERY) { dev_dbg(&isp->pdev->dev, "RST_PHASE2 retry csr_in = %x\n", val); @@ -160,8 +156,8 @@ int ipu6_buttress_ipc_reset(struct ipu6_device *isp, * 2) Set peer CSR bit * IPC_PEER_COMP_ACTIONS_RST_PHASE1 */ - writel(QUERY, isp->base + ipc->csr_in); - writel(ENTRY, isp->base + ipc->csr_out); + writel(QUERY, isp->base + regs->csr_in); + writel(ENTRY, isp->base + regs->csr_out); break; default: dev_dbg_ratelimited(&isp->pdev->dev, @@ -176,42 +172,42 @@ int ipu6_buttress_ipc_reset(struct ipu6_device *isp, return -ETIMEDOUT; } -static void ipu6_buttress_ipc_validity_close(struct ipu6_device *isp, - struct ipu6_buttress_ipc *ipc) +static void ipu6_buttress_ipc_validity_close(struct ipu6_device *isp) { writel(BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ, - isp->base + ipc->csr_out); + isp->base + isp->buttress.regs->csr_out); } static int -ipu6_buttress_ipc_validity_open(struct ipu6_device *isp, - struct ipu6_buttress_ipc *ipc) +ipu6_buttress_ipc_validity_open(struct ipu6_device *isp) { unsigned int mask = BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID; + const struct ipu6_buttress_registers *regs = isp->buttress.regs; void __iomem *addr; int ret; u32 val; writel(BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ, - isp->base + ipc->csr_out); + isp->base + regs->csr_out); - addr = isp->base + ipc->csr_in; + addr = isp->base + regs->csr_in; ret = readl_poll_timeout(addr, val, val & mask, 200, BUTTRESS_IPC_VALIDITY_TIMEOUT_US); if (ret) { dev_err(&isp->pdev->dev, "CSE validity timeout 0x%x\n", val); - ipu6_buttress_ipc_validity_close(isp, ipc); + ipu6_buttress_ipc_validity_close(isp); } return ret; } -static void ipu6_buttress_ipc_recv(struct ipu6_device *isp, - struct ipu6_buttress_ipc *ipc, u32 *ipc_msg) +static void ipu6_buttress_ipc_recv(struct ipu6_device *isp, u32 *ipc_msg) { + const struct ipu6_buttress_registers *regs = isp->buttress.regs; + if (ipc_msg) - *ipc_msg = readl(isp->base + ipc->data0_in); - writel(0, isp->base + ipc->db0_in); + *ipc_msg = readl(isp->base + regs->data0_in); + writel(0, isp->base + regs->db0_in); } static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp, @@ -221,14 +217,15 @@ static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp, unsigned long tx_timeout_jiffies, rx_timeout_jiffies; unsigned int i, retry = BUTTRESS_IPC_CMD_SEND_RETRY; struct ipu6_buttress *b = &isp->buttress; - struct ipu6_buttress_ipc *ipc = &b->cse; + struct ipu6_buttress_ipc *ipc = &b->ipc; + const struct ipu6_buttress_registers *regs = b->regs; u32 val; int ret; int tout; mutex_lock(&b->ipc_mutex); - ret = ipu6_buttress_ipc_validity_open(isp, ipc); + ret = ipu6_buttress_ipc_validity_open(isp); if (ret) { dev_err(&isp->pdev->dev, "IPC validity open failed\n"); goto out; @@ -244,9 +241,9 @@ static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp, dev_dbg(&isp->pdev->dev, "bulk IPC command: 0x%x\n", msgs[i].cmd); - writel(msgs[i].cmd, isp->base + ipc->data0_out); + writel(msgs[i].cmd, isp->base + regs->data0_out); val = BUTTRESS_IU2CSEDB0_BUSY | msgs[i].cmd_size; - writel(val, isp->base + ipc->db0_out); + writel(val, isp->base + regs->db0_out); tout = wait_for_completion_timeout(&ipc->send_complete, tx_timeout_jiffies); @@ -258,7 +255,7 @@ static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp, } /* Try again if CSE is not responding on first try */ - writel(0, isp->base + ipc->db0_out); + writel(0, isp->base + regs->db0_out); i--; continue; } @@ -276,8 +273,8 @@ static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp, goto out; } - if (ipc->nack_mask && - (ipc->recv_data & ipc->nack_mask) == ipc->nack) { + if ((ipc->recv_data & BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK) == + BUTTRESS_CSE2IUDATA0_IPC_NACK) { dev_err(&isp->pdev->dev, "IPC NACK for cmd 0x%x\n", msgs[i].cmd); ret = -EIO; @@ -296,7 +293,7 @@ static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp, dev_dbg(&isp->pdev->dev, "bulk IPC commands done\n"); out: - ipu6_buttress_ipc_validity_close(isp, ipc); + ipu6_buttress_ipc_validity_close(isp); mutex_unlock(&b->ipc_mutex); return ret; } @@ -337,7 +334,8 @@ irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr) struct ipu6_device *isp = isp_ptr; struct ipu6_bus_device *adev[] = { isp->isys, isp->psys }; struct ipu6_buttress *b = &isp->buttress; - u32 reg_irq_sts = BUTTRESS_REG_ISR_STATUS; + const struct ipu6_buttress_registers *regs = b->regs; + const u32 adev_irq_mask[] = { regs->irq_is, regs->irq_ps }; irqreturn_t ret = IRQ_NONE; u32 disable_irqs = 0; u32 irq_status; @@ -348,7 +346,14 @@ irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr) if (!active) return IRQ_NONE; - irq_status = readl(isp->base + reg_irq_sts); + if (IS_IPU7(isp)) { + u32 pb_irq; + + pb_irq = readl(isp->pb_base + IPU7_PB_INTERRUPT_STATUS); + writel(pb_irq, isp->pb_base + IPU7_PB_INTERRUPT_STATUS); + } + + irq_status = readl(isp->base + regs->irq_status); if (irq_status == 0 || WARN_ON_ONCE(irq_status == 0xffffffffu)) { if (active > 0) pm_runtime_put_noidle(&isp->pdev->dev); @@ -356,39 +361,40 @@ irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr) } do { - writel(irq_status, isp->base + BUTTRESS_REG_ISR_CLEAR); + writel(irq_status, isp->base + regs->irq_clear); - for (i = 0; i < ARRAY_SIZE(ipu6_adev_irq_mask); i++) { + for (i = 0; i < ARRAY_SIZE(adev_irq_mask); i++) { irqreturn_t r = ipu6_buttress_call_isr(adev[i]); - if (!(irq_status & ipu6_adev_irq_mask[i])) + if (!(irq_status & adev_irq_mask[i])) continue; if (r == IRQ_WAKE_THREAD) { ret = IRQ_WAKE_THREAD; - disable_irqs |= ipu6_adev_irq_mask[i]; + disable_irqs |= adev_irq_mask[i]; } else if (ret == IRQ_NONE && r == IRQ_HANDLED) { ret = IRQ_HANDLED; } } - if ((irq_status & BUTTRESS_EVENT) && ret == IRQ_NONE) + if ((irq_status & regs->irq_events) && ret == IRQ_NONE) ret = IRQ_HANDLED; - if (irq_status & BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING) { + if (irq_status & regs->irq_cse_ipc) { dev_dbg(&isp->pdev->dev, "BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING\n"); - ipu6_buttress_ipc_recv(isp, &b->cse, &b->cse.recv_data); - complete(&b->cse.recv_complete); + + ipu6_buttress_ipc_recv(isp, &b->ipc.recv_data); + complete(&b->ipc.recv_complete); } - if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE) { + if (irq_status & regs->irq_exec_done) { dev_dbg(&isp->pdev->dev, "BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n"); - complete(&b->cse.send_complete); + complete(&b->ipc.send_complete); } - if (irq_status & BUTTRESS_ISR_SAI_VIOLATION && + if (irq_status & regs->irq_sai && ipu6_buttress_get_secure_mode(isp)) dev_err(&isp->pdev->dev, "BUTTRESS_ISR_SAI_VIOLATION\n"); @@ -407,12 +413,12 @@ irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr) break; } - irq_status = readl(isp->base + reg_irq_sts); + irq_status = readl(isp->base + regs->irq_status); } while (irq_status); if (disable_irqs) - writel(BUTTRESS_IRQS & ~disable_irqs, - isp->base + BUTTRESS_REG_ISR_ENABLE); + writel(regs->irq_all & ~disable_irqs, + isp->base + regs->irq_enable); if (active > 0) pm_runtime_put(&isp->pdev->dev); @@ -423,12 +429,13 @@ irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr) irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr) { struct ipu6_device *isp = isp_ptr; + const struct ipu6_buttress_registers *regs = isp->buttress.regs; struct ipu6_bus_device *adev[] = { isp->isys, isp->psys }; const struct ipu6_auxdrv_data *drv_data = NULL; irqreturn_t ret = IRQ_NONE; unsigned int i; - for (i = 0; i < ARRAY_SIZE(ipu6_adev_irq_mask) && adev[i]; i++) { + for (i = 0; i < ARRAY_SIZE(adev) && adev[i]; i++) { drv_data = adev[i]->auxdrv_data; if (!drv_data) continue; @@ -438,46 +445,179 @@ irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr) ret = IRQ_HANDLED; } - writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE); + writel(regs->irq_all, isp->base + regs->irq_enable); return ret; } -int ipu6_buttress_power(struct device *dev, - const struct ipu6_buttress_ctrl *ctrl, bool on) +static int ipu7_isys_d2d_power(struct ipu6_device *isp, bool on) +{ + u32 target = on ? IPU7_BUTTRESS_D2D_PWR_ACK : 0U; + u32 val; + int ret; + + val = readl(isp->base + IPU7_BUTTRESS_REG_D2D_CTL); + if ((val & IPU7_BUTTRESS_D2D_PWR_ACK) == target) + return 0; + + if (on) + val |= IPU7_BUTTRESS_D2D_PWR_EN; + else + val &= ~IPU7_BUTTRESS_D2D_PWR_EN; + writel(val, isp->base + IPU7_BUTTRESS_REG_D2D_CTL); + + ret = readl_poll_timeout(isp->base + IPU7_BUTTRESS_REG_D2D_CTL, val, + (val & IPU7_BUTTRESS_D2D_PWR_ACK) == target, + 100, BUTTRESS_POWER_TIMEOUT_US); + if (ret) + dev_err(&isp->pdev->dev, "D2D power %s timeout: 0x%x\n", + on ? "up" : "down", val); + + return ret; +} + +static void ipu7_nde_control(struct ipu6_device *isp, bool on) +{ + u32 val; + + val = FIELD_PREP(IPU7_NDE_VAL_MASK, + on ? IPU7_NDE_VAL_ACTIVE : IPU7_NDE_VAL_DEFAULT) | + FIELD_PREP(IPU7_NDE_SCALE_MASK, + on ? IPU7_NDE_SCALE_ACTIVE : IPU7_NDE_SCALE_DEFAULT) | + FIELD_PREP(IPU7_NDE_VALID_MASK, + on ? IPU7_NDE_VALID_ACTIVE : IPU7_NDE_VALID_DEFAULT) | + FIELD_PREP(IPU7_NDE_RESVEC_MASK, IPU7_NDE_RESVEC); + writel(val, isp->base + IPU7_BUTTRESS_REG_NDE_CONTROL); +} + +static int __ipu7_power_on(struct device *dev, + const struct ipu6_buttress_ctrl *ctrl) +{ + struct ipu6_device *isp = to_ipu6_bus_device(dev)->isp; + bool is_isys = ctrl->subsys_id == IPU_ISYS; + u32 pwr_sts, val, ovrd_clk, slp, own_clk_ack; + int ret; + + val = ctrl->ratio | (IPU7_FREQ_CTL_CDYN << IPU7_FREQ_CTL_CDYN_SHIFT); + pwr_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift; + + if (is_isys) { + ret = ipu7_isys_d2d_power(isp, true); + if (ret) + return ret; + + ipu7_nde_control(isp, true); + } + + /* Request clock ownership. */ + ovrd_clk = is_isys ? IPU7_BUTTRESS_OVERRIDE_IS_CLK : + IPU7_BUTTRESS_OVERRIDE_PS_CLK; + + slp = readl(isp->base + IPU7_BUTTRESS_REG_SLEEP_LEVEL_CFG); + writel(slp | ovrd_clk, isp->base + IPU7_BUTTRESS_REG_SLEEP_LEVEL_CFG); + + own_clk_ack = is_isys ? IPU7_BUTTRESS_OWN_ACK_IS_CLK : + IPU7_BUTTRESS_OWN_ACK_PS_CLK; + ret = readl_poll_timeout(isp->base + IPU7_BUTTRESS_REG_SLEEP_LEVEL_STS, + slp, (slp & own_clk_ack), + 100, BUTTRESS_POWER_TIMEOUT_US); + if (ret) + dev_warn(&isp->pdev->dev, "clock ownership timeout: 0x%x\n", + slp); + + writel(val, isp->base + ctrl->freq_ctl); + + ret = readl_poll_timeout(isp->base + isp->buttress.regs->pwr_status, + val, (val & ctrl->pwr_sts_mask) == pwr_sts, + 100, BUTTRESS_POWER_TIMEOUT_US); + if (ret) { + dev_err(&isp->pdev->dev, + "Change power status timeout with 0x%x\n", val); + return ret; + } + + slp = readl(isp->base + IPU7_BUTTRESS_REG_SLEEP_LEVEL_CFG); + writel(slp & ~ovrd_clk, isp->base + IPU7_BUTTRESS_REG_SLEEP_LEVEL_CFG); + + return 0; +} + +static int __ipu7_power_off(struct device *dev, + const struct ipu6_buttress_ctrl *ctrl) { struct ipu6_device *isp = to_ipu6_bus_device(dev)->isp; u32 pwr_sts, val; int ret; - if (!ctrl) - return 0; + writel(0x8U, isp->base + ctrl->freq_ctl); - mutex_lock(&isp->buttress.power_mutex); + pwr_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift; + ret = readl_poll_timeout(isp->base + isp->buttress.regs->pwr_status, + val, (val & ctrl->pwr_sts_mask) == pwr_sts, + 100, BUTTRESS_POWER_TIMEOUT_US); + if (ret) { + dev_err(&isp->pdev->dev, + "Change power status timeout with 0x%x\n", val); + return ret; + } + + if (ctrl->subsys_id == IPU_ISYS) { + ipu7_isys_d2d_power(isp, false); + ipu7_nde_control(isp, false); + } + + return 0; +} + +static int __ipu6_power(struct device *dev, + const struct ipu6_buttress_ctrl *ctrl, bool on) +{ + struct ipu6_device *isp = to_ipu6_bus_device(dev)->isp; + u32 pwr_sts, val; + int ret; if (!on) { val = 0; pwr_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift; } else { val = BUTTRESS_FREQ_CTL_START | - FIELD_PREP(BUTTRESS_FREQ_CTL_RATIO_MASK, - ctrl->ratio) | - FIELD_PREP(BUTTRESS_FREQ_CTL_QOS_FLOOR_MASK, - ctrl->qos_floor) | - BUTTRESS_FREQ_CTL_ICCMAX_LEVEL; + FIELD_PREP(BUTTRESS_FREQ_CTL_RATIO_MASK, ctrl->ratio) | + FIELD_PREP(BUTTRESS_FREQ_CTL_QOS_FLOOR_MASK, + ctrl->qos_floor) | + BUTTRESS_FREQ_CTL_ICCMAX_LEVEL; pwr_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift; } writel(val, isp->base + ctrl->freq_ctl); - ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE, + ret = readl_poll_timeout(isp->base + isp->buttress.regs->pwr_status, val, (val & ctrl->pwr_sts_mask) == pwr_sts, 100, BUTTRESS_POWER_TIMEOUT_US); if (ret) dev_err(&isp->pdev->dev, "Change power status timeout with 0x%x\n", val); + return ret; +} + +int ipu6_buttress_power(struct device *dev, + const struct ipu6_buttress_ctrl *ctrl, bool on) +{ + struct ipu6_device *isp = to_ipu6_bus_device(dev)->isp; + int ret; + + if (!ctrl) + return 0; + + mutex_lock(&isp->buttress.power_mutex); + + if (IS_IPU7(isp)) + ret = on ? __ipu7_power_on(dev, ctrl) : + __ipu7_power_off(dev, ctrl); + else + ret = __ipu6_power(dev, ctrl, on); + mutex_unlock(&isp->buttress.power_mutex); return ret; @@ -487,7 +627,7 @@ bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp) { u32 val; - val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); + val = readl(isp->base + isp->buttress.regs->security_ctl); return val & BUTTRESS_SECURITY_CTL_FW_SECURE_MODE; } @@ -499,7 +639,7 @@ bool ipu6_buttress_auth_done(struct ipu6_device *isp) if (!isp->secure_mode) return true; - val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); + val = readl(isp->base + isp->buttress.regs->security_ctl); val = FIELD_GET(BUTTRESS_SECURITY_CTL_FW_SETUP_MASK, val); return val == BUTTRESS_SECURITY_CTL_AUTH_DONE; @@ -517,10 +657,10 @@ int ipu6_buttress_reset_authentication(struct ipu6_device *isp) } writel(BUTTRESS_FW_RESET_CTL_START, isp->base + - BUTTRESS_REG_FW_RESET_CTL); + isp->buttress.regs->fw_reset_ctl); - ret = readl_poll_timeout(isp->base + BUTTRESS_REG_FW_RESET_CTL, val, - val & BUTTRESS_FW_RESET_CTL_DONE, 500, + ret = readl_poll_timeout(isp->base + isp->buttress.regs->fw_reset_ctl, + val, val & BUTTRESS_FW_RESET_CTL_DONE, 500, BUTTRESS_CSE_FWRESET_TIMEOUT_US); if (ret) { dev_err(&isp->pdev->dev, @@ -529,62 +669,63 @@ int ipu6_buttress_reset_authentication(struct ipu6_device *isp) } dev_dbg(&isp->pdev->dev, "FW reset for authentication done\n"); - writel(0, isp->base + BUTTRESS_REG_FW_RESET_CTL); + writel(0, isp->base + isp->buttress.regs->fw_reset_ctl); + /* leave some time for HW restore */ usleep_range(800, 1000); return 0; } -int ipu6_buttress_map_fw_image(struct ipu6_bus_device *sys, - const struct firmware *fw, struct sg_table *sgt) +int ipu6_map_fw_region(struct ipu6_bus_device *sys, const void *data, + size_t size, enum dma_data_direction dir, + unsigned long attrs) { - bool is_vmalloc = is_vmalloc_addr(fw->data); + bool is_vmalloc = is_vmalloc_addr(data); struct pci_dev *pdev = sys->isp->pdev; + struct sg_table *sgt = &sys->fw_sgt; struct page **pages; - const void *addr; unsigned long n_pages; unsigned int i; int ret; - if (!is_vmalloc && !virt_addr_valid(fw->data)) + if (!is_vmalloc && !virt_addr_valid(data)) return -EDOM; - n_pages = PFN_UP(fw->size); + n_pages = PFN_UP(size); pages = kmalloc_objs(*pages, n_pages); if (!pages) return -ENOMEM; - addr = fw->data; for (i = 0; i < n_pages; i++) { struct page *p = is_vmalloc ? - vmalloc_to_page(addr) : virt_to_page(addr); + vmalloc_to_page(data) : virt_to_page(data); if (!p) { ret = -ENOMEM; goto out; } pages[i] = p; - addr += PAGE_SIZE; + data += PAGE_SIZE; } - ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, fw->size, + ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, size, GFP_KERNEL); if (ret) { ret = -ENOMEM; goto out; } - ret = dma_map_sgtable(&pdev->dev, sgt, DMA_TO_DEVICE, 0); + ret = dma_map_sgtable(&pdev->dev, sgt, dir, 0); if (ret) { sg_free_table(sgt); goto out; } - ret = ipu6_dma_map_sgtable(sys, sgt, DMA_TO_DEVICE, 0); + ret = ipu6_dma_map_sgtable(sys, sgt, dir, attrs); if (ret) { - dma_unmap_sgtable(&pdev->dev, sgt, DMA_TO_DEVICE, 0); + dma_unmap_sgtable(&pdev->dev, sgt, dir, 0); sg_free_table(sgt); goto out; } @@ -596,18 +737,18 @@ out: return ret; } -EXPORT_SYMBOL_NS_GPL(ipu6_buttress_map_fw_image, "INTEL_IPU6"); +EXPORT_SYMBOL_NS_GPL(ipu6_map_fw_region, "INTEL_IPU6"); -void ipu6_buttress_unmap_fw_image(struct ipu6_bus_device *sys, - struct sg_table *sgt) +void ipu6_unmap_fw_region(struct ipu6_bus_device *sys, + enum dma_data_direction dir) { struct pci_dev *pdev = sys->isp->pdev; - ipu6_dma_unmap_sgtable(sys, sgt, DMA_TO_DEVICE, 0); - dma_unmap_sgtable(&pdev->dev, sgt, DMA_TO_DEVICE, 0); - sg_free_table(sgt); + ipu6_dma_unmap_sgtable(sys, &sys->fw_sgt, dir, 0); + dma_unmap_sgtable(&pdev->dev, &sys->fw_sgt, dir, 0); + sg_free_table(&sys->fw_sgt); } -EXPORT_SYMBOL_NS_GPL(ipu6_buttress_unmap_fw_image, "INTEL_IPU6"); +EXPORT_SYMBOL_NS_GPL(ipu6_unmap_fw_region, "INTEL_IPU6"); int ipu6_buttress_authenticate(struct ipu6_device *isp) { @@ -634,11 +775,18 @@ int ipu6_buttress_authenticate(struct ipu6_device *isp) * Write address of FIT table to FW_SOURCE register * Let's use fw address. I.e. not using FIT table yet */ - data = lower_32_bits(isp->psys->pkg_dir_dma_addr); - writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_LO); + if (IS_IPU7(isp)) { + writel(isp->cpd_fw->size, + isp->base + IPU7_BUTTRESS_REG_FW_SOURCE_SIZE); + writel(sg_dma_address(isp->psys->fw_sgt.sgl), + isp->base + IPU7_BUTTRESS_REG_FW_SOURCE_BASE); + } else { + data = lower_32_bits(isp->psys->pkg_dir_dma_addr); + writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_LO); - data = upper_32_bits(isp->psys->pkg_dir_dma_addr); - writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_HI); + data = upper_32_bits(isp->psys->pkg_dir_dma_addr); + writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_HI); + } /* * Write boot_load into IU2CSEDATA0 @@ -659,7 +807,7 @@ int ipu6_buttress_authenticate(struct ipu6_device *isp) mask = BUTTRESS_SECURITY_CTL_FW_SETUP_MASK; done = BUTTRESS_SECURITY_CTL_FW_SETUP_DONE; fail = BUTTRESS_SECURITY_CTL_AUTH_FAILED; - ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data, + ret = readl_poll_timeout(isp->base + b->regs->security_ctl, data, ((data & mask) == done || (data & mask) == fail), 500, BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US); @@ -674,8 +822,11 @@ int ipu6_buttress_authenticate(struct ipu6_device *isp) goto out_unlock; } - ret = readl_poll_timeout(psys_pdata->base + BOOTLOADER_STATUS_OFFSET, - data, data == BOOTLOADER_MAGIC_KEY, 500, + void __iomem *base = IS_IPU7(isp) ? + isp->base + IPU7_BUTTRESS_REG_FW_BOOT_PARAMS7 : + psys_pdata->base + BOOTLOADER_STATUS_OFFSET; + + ret = readl_poll_timeout(base, data, data == BOOTLOADER_MAGIC_KEY, 500, BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US); if (ret) { dev_err(&isp->pdev->dev, "Unexpected magic number 0x%x\n", @@ -699,7 +850,7 @@ int ipu6_buttress_authenticate(struct ipu6_device *isp) } done = BUTTRESS_SECURITY_CTL_AUTH_DONE; - ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data, + ret = readl_poll_timeout(isp->base + b->regs->security_ctl, data, ((data & mask) == done || (data & mask) == fail), 500, BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US); @@ -724,15 +875,16 @@ out_unlock: static int ipu6_buttress_send_tsc_request(struct ipu6_device *isp) { + const struct ipu6_buttress_registers *regs = isp->buttress.regs; u32 val, mask, done; int ret; mask = BUTTRESS_PWR_STATE_HH_STATUS_MASK; writel(BUTTRESS_FABRIC_CMD_START_TSC_SYNC, - isp->base + BUTTRESS_REG_FABRIC_CMD); + isp->base + regs->fabric_cmd); - val = readl(isp->base + BUTTRESS_REG_PWR_STATE); + val = readl(isp->base + regs->pwr_status); val = FIELD_GET(mask, val); if (val == BUTTRESS_PWR_STATE_HH_STATE_ERR) { dev_err(&isp->pdev->dev, "Start tsc sync failed\n"); @@ -740,8 +892,8 @@ static int ipu6_buttress_send_tsc_request(struct ipu6_device *isp) } done = BUTTRESS_PWR_STATE_HH_STATE_DONE; - ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE, val, - FIELD_GET(mask, val) == done, 500, + ret = readl_poll_timeout(isp->base + regs->pwr_status, + val, FIELD_GET(mask, val) == done, 500, BUTTRESS_TSC_SYNC_TIMEOUT_US); if (ret) dev_err(&isp->pdev->dev, "Start tsc sync timeout\n"); @@ -749,11 +901,30 @@ static int ipu6_buttress_send_tsc_request(struct ipu6_device *isp) return ret; } -int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp) +static int __ipu7p5_start_tsc_sync(struct ipu6_device *isp) { - unsigned int i; + u32 val; + + val = readl(isp->base + IPU7_BUTTRESS_REG_TSC_CTL); + val |= IPU7_BUTTRESS_SEL_PB_TIMESTAMP; + writel(val, isp->base + IPU7_BUTTRESS_REG_TSC_CTL); + + for (unsigned int i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { + val = readl(isp->base + IPU7_BUTTRESS_REG_PB_TIMESTAMP_VALID); + if (val == 1) + return 0; + + usleep_range(40, 50); + } + + dev_err(&isp->pdev->dev, "TSC sync failed (timeout)\n"); - for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { + return -ETIMEDOUT; +} + +static int __ipu6_start_tsc_sync(struct ipu6_device *isp) +{ + for (unsigned int i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { u32 val; int ret; @@ -761,28 +932,39 @@ int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp) if (ret != -ETIMEDOUT) return ret; - val = readl(isp->base + BUTTRESS_REG_TSW_CTL); + u32 tsw_ctl = isp->buttress.regs->tsw_ctl; + + val = readl(isp->base + tsw_ctl); val = val | BUTTRESS_TSW_CTL_SOFT_RESET; - writel(val, isp->base + BUTTRESS_REG_TSW_CTL); + writel(val, isp->base + tsw_ctl); val = val & ~BUTTRESS_TSW_CTL_SOFT_RESET; - writel(val, isp->base + BUTTRESS_REG_TSW_CTL); + writel(val, isp->base + tsw_ctl); } dev_err(&isp->pdev->dev, "TSC sync failed (timeout)\n"); return -ETIMEDOUT; } + +int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp) +{ + if (IS_IPU7P5(isp)) + return __ipu7p5_start_tsc_sync(isp); + + return __ipu6_start_tsc_sync(isp); +} EXPORT_SYMBOL_NS_GPL(ipu6_buttress_start_tsc_sync, "INTEL_IPU6"); void ipu6_buttress_tsc_read(struct ipu6_device *isp, u64 *val) { + void __iomem *tsc = isp->base + isp->buttress.regs->tsc_lo; u32 tsc_hi_1, tsc_hi_2, tsc_lo; unsigned long flags; local_irq_save(flags); - tsc_hi_1 = readl(isp->base + BUTTRESS_REG_TSC_HI); - tsc_lo = readl(isp->base + BUTTRESS_REG_TSC_LO); - tsc_hi_2 = readl(isp->base + BUTTRESS_REG_TSC_HI); + tsc_hi_1 = readl(tsc + BUTTRESS_TSC_HI_OFFSET); + tsc_lo = readl(tsc); + tsc_hi_2 = readl(tsc + BUTTRESS_TSC_HI_OFFSET); if (tsc_hi_1 == tsc_hi_2) { *val = (u64)tsc_hi_1 << 32 | tsc_lo; } else { @@ -811,13 +993,77 @@ u64 ipu6_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu6_device *isp) } EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_ticks_to_ns, "INTEL_IPU6"); +/* trigger uc control to wakeup fw */ +void ipu7_buttress_wakeup_isys(const struct ipu6_device *isp) +{ + u32 val; + + val = readl(isp->base + IPU7_BUTTRESS_REG_ISYS_UCX_CTRL_STATUS); + val |= IPU7_UCX_CTL_WAKEUP; + writel(val, isp->base + IPU7_BUTTRESS_REG_ISYS_UCX_CTRL_STATUS); +} +EXPORT_SYMBOL_NS_GPL(ipu7_buttress_wakeup_isys, "INTEL_IPU6"); + +u32 ipu7_buttress_get_isys_freq(struct ipu6_device *isp) +{ + u32 val; + + val = readl(isp->base + IPU7_BUTTRESS_REG_IS_WORKPOINT_REQ); + val &= IPU7_BUTTRESS_IS_FREQ_CTL_RATIO_MASK; + + return val * 50 / 3; +} +EXPORT_SYMBOL_NS_GPL(ipu7_buttress_get_isys_freq, "INTEL_IPU6"); + +static const struct x86_cpu_id ipu7_misc_cfg_exclusion[] = { + X86_MATCH_VFM_STEPS(INTEL_PANTHERLAKE_L, 0x1, 0x1, 0), + {}, +}; + +#define IPU7_WRXREQOP_OVRD_VAL_MASK GENMASK(22, 19) + +static void ipu7_buttress_setup(struct ipu6_device *isp) +{ + struct ipu6_buttress *b = &isp->buttress; + u32 val; + + /* program PB BAR */ + writel(0, isp->pb_base + IPU7_GLOBAL_INTERRUPT_MASK); + val = readl(isp->pb_base + IPU7_BAR2_MISC_CONFIG); + + val |= 0x100U; + if (!IS_IPU7_MTL(isp) && !x86_match_cpu(ipu7_misc_cfg_exclusion)) + val |= FIELD_PREP(IPU7_WRXREQOP_OVRD_VAL_MASK, 0xf) | BIT(18); + + writel(val, isp->pb_base + IPU7_BAR2_MISC_CONFIG); + + if (IS_IPU7P5(isp)) { + writel(BIT(14), isp->pb_base + IPU7_TLBID_HASH_ENABLE_63_32); + writel(BIT(9), isp->pb_base + IPU7_TLBID_HASH_ENABLE_95_64); + } else { + writel(BIT(22), isp->pb_base + IPU7_TLBID_HASH_ENABLE_63_32); + writel(BIT(1), isp->pb_base + IPU7_TLBID_HASH_ENABLE_127_96); + } + + writel(b->regs->irq_all, isp->base + b->regs->irq_clear); + writel(b->regs->irq_all, isp->base + IPU7_BUTTRESS_REG_IRQ_MASK); + writel(b->regs->irq_all, isp->base + b->regs->irq_enable); + + /* LNL SW workaround for PS PD hang when PS sub-domain during PD */ + writel(IPU7_BUTTRESS_CG_CTRL_PS_FSM_CG, isp->base + IPU7_BUTTRESS_REG_CG_CTRL_BITS); +} + void ipu6_buttress_restore(struct ipu6_device *isp) { struct ipu6_buttress *b = &isp->buttress; - writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR); - writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE); - writel(b->wdt_cached_value, isp->base + BUTTRESS_REG_WDT); + if (IS_IPU7(isp)) { + ipu7_buttress_setup(isp); + } else { + writel(b->regs->irq_all, isp->base + b->regs->irq_clear); + writel(b->regs->irq_all, isp->base + b->regs->irq_enable); + } + writel(b->wdt_cached_value, isp->base + b->regs->wdt); } int ipu6_buttress_init(struct ipu6_device *isp) @@ -830,54 +1076,52 @@ int ipu6_buttress_init(struct ipu6_device *isp) mutex_init(&b->auth_mutex); mutex_init(&b->cons_mutex); mutex_init(&b->ipc_mutex); - init_completion(&b->cse.send_complete); - init_completion(&b->cse.recv_complete); - - b->cse.nack = BUTTRESS_CSE2IUDATA0_IPC_NACK; - b->cse.nack_mask = BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK; - b->cse.csr_in = BUTTRESS_REG_CSE2IUCSR; - b->cse.csr_out = BUTTRESS_REG_IU2CSECSR; - b->cse.db0_in = BUTTRESS_REG_CSE2IUDB0; - b->cse.db0_out = BUTTRESS_REG_IU2CSEDB0; - b->cse.data0_in = BUTTRESS_REG_CSE2IUDATA0; - b->cse.data0_out = BUTTRESS_REG_IU2CSEDATA0; + init_completion(&b->ipc.send_complete); + init_completion(&b->ipc.recv_complete); INIT_LIST_HEAD(&b->constraints); - isp->secure_mode = ipu6_buttress_get_secure_mode(isp); - dev_dbg(&isp->pdev->dev, "IPU6 in %s mode touch 0x%x mask 0x%x\n", - isp->secure_mode ? "secure" : "non-secure", - readl(isp->base + BUTTRESS_REG_SECURITY_TOUCH), - readl(isp->base + BUTTRESS_REG_CAMERA_MASK)); - - b->wdt_cached_value = readl(isp->base + BUTTRESS_REG_WDT); - writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR); - writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE); - - /* get ref_clk frequency by reading the indication in btrs control */ - val = readl(isp->base + BUTTRESS_REG_BTRS_CTRL); - val = FIELD_GET(BUTTRESS_REG_BTRS_CTRL_REF_CLK_IND, val); - - switch (val) { - case 0x0: - b->ref_clk = 240; - break; - case 0x1: - b->ref_clk = 192; - break; - case 0x2: + + dev_dbg(&isp->pdev->dev, "IPU in %s mode\n", + isp->secure_mode ? "secure" : "non-secure"); + + if (IS_IPU7(isp)) { + ipu7_buttress_setup(isp); b->ref_clk = 384; - break; - default: - dev_warn(&isp->pdev->dev, - "Unsupported ref clock, use 19.2Mhz by default.\n"); - b->ref_clk = 192; - break; + } else { + dev_dbg(&isp->pdev->dev, "IPU6 touch 0x%x mask 0x%x\n", + readl(isp->base + BUTTRESS_REG_SECURITY_TOUCH), + readl(isp->base + BUTTRESS_REG_CAMERA_MASK)); + + writel(b->regs->irq_all, isp->base + b->regs->irq_clear); + writel(b->regs->irq_all, isp->base + b->regs->irq_enable); + + /* get ref_clk frequency by reading the indication in btrs control */ + val = readl(isp->base + b->regs->btrs_ctrl); + val = FIELD_GET(BUTTRESS_REG_BTRS_CTRL_REF_CLK_IND, val); + + switch (val) { + case 0x0: + b->ref_clk = 240; + break; + case 0x1: + b->ref_clk = 192; + break; + case 0x2: + b->ref_clk = 384; + break; + default: + dev_warn(&isp->pdev->dev, + "Unsupported ref clock, use 19.2Mhz by default.\n"); + b->ref_clk = 192; + break; + } } + b->wdt_cached_value = readl(isp->base + b->regs->wdt); /* Retry couple of times in case of CSE initialization is delayed */ do { - ret = ipu6_buttress_ipc_reset(isp, &b->cse); + ret = ipu6_buttress_ipc_reset(isp); if (ret) { dev_warn(&isp->pdev->dev, "IPC reset protocol failed, retrying\n"); @@ -901,7 +1145,7 @@ void ipu6_buttress_exit(struct ipu6_device *isp) { struct ipu6_buttress *b = &isp->buttress; - writel(0, isp->base + BUTTRESS_REG_ISR_ENABLE); + writel(0, isp->base + b->regs->irq_enable); mutex_destroy(&b->power_mutex); mutex_destroy(&b->auth_mutex); diff --git a/drivers/media/pci/intel/ipu6/ipu6-buttress.h b/drivers/media/pci/intel/ipu6/ipu6-buttress.h index 51e5ad48db82..79d8f5893686 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-buttress.h +++ b/drivers/media/pci/intel/ipu6/ipu6-buttress.h @@ -26,25 +26,50 @@ struct ipu6_buttress_ctrl { u32 freq_ctl, pwr_sts_shift, pwr_sts_mask, pwr_sts_on, pwr_sts_off; unsigned int ratio; unsigned int qos_floor; + int subsys_id; }; struct ipu6_buttress_ipc { struct completion send_complete; struct completion recv_complete; - u32 nack; - u32 nack_mask; u32 recv_data; - u32 csr_out; +}; + +struct ipu6_buttress_registers { + /* Registers */ + u32 irq_status; + u32 irq_clear; + u32 irq_enable; + u32 pwr_status; + u32 security_ctl; + u32 fw_reset_ctl; + u32 fabric_cmd; + u32 tsw_ctl; + u32 tsc_lo; + u32 wdt; + u32 btrs_ctrl; u32 csr_in; + u32 csr_out; u32 db0_in; u32 db0_out; - u32 data0_out; u32 data0_in; + u32 data0_out; + u32 sku_id; + + /* Bitmasks */ + u32 irq_is; + u32 irq_ps; + u32 irq_all; + u32 irq_events; + u32 irq_cse_ipc; + u32 irq_exec_done; + u32 irq_sai; }; struct ipu6_buttress { struct mutex power_mutex, auth_mutex, cons_mutex, ipc_mutex; - struct ipu6_buttress_ipc cse; + struct ipu6_buttress_ipc ipc; + const struct ipu6_buttress_registers *regs; struct list_head constraints; u32 wdt_cached_value; bool force_suspend; @@ -58,13 +83,12 @@ struct ipu6_ipc_buttress_bulk_msg { u8 cmd_size; }; -int ipu6_buttress_ipc_reset(struct ipu6_device *isp, - struct ipu6_buttress_ipc *ipc); -int ipu6_buttress_map_fw_image(struct ipu6_bus_device *sys, - const struct firmware *fw, - struct sg_table *sgt); -void ipu6_buttress_unmap_fw_image(struct ipu6_bus_device *sys, - struct sg_table *sgt); +int ipu6_buttress_ipc_reset(struct ipu6_device *isp); +int ipu6_map_fw_region(struct ipu6_bus_device *sys, const void *data, + size_t size, enum dma_data_direction dir, + unsigned long attrs); +void ipu6_unmap_fw_region(struct ipu6_bus_device *sys, + enum dma_data_direction dir); int ipu6_buttress_power(struct device *dev, const struct ipu6_buttress_ctrl *ctrl, bool on); bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp); @@ -82,4 +106,6 @@ void ipu6_buttress_exit(struct ipu6_device *isp); void ipu6_buttress_csi_port_config(struct ipu6_device *isp, u32 legacy, u32 combo); void ipu6_buttress_restore(struct ipu6_device *isp); +void ipu7_buttress_wakeup_isys(const struct ipu6_device *isp); +u32 ipu7_buttress_get_isys_freq(struct ipu6_device *isp); #endif /* IPU6_BUTTRESS_H */ diff --git a/drivers/media/pci/intel/ipu6/ipu6-cpd.c b/drivers/media/pci/intel/ipu6/ipu6-cpd.c index b7013f6524ec..1fadb0f2f6c1 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-cpd.c +++ b/drivers/media/pci/intel/ipu6/ipu6-cpd.c @@ -16,6 +16,7 @@ #include "ipu6-bus.h" #include "ipu6-cpd.h" #include "ipu6-dma.h" +#include "ipu7-mmu-hw.h" /* 15 entries + header*/ #define MAX_PKG_DIR_ENT_CNT 16 @@ -62,6 +63,20 @@ static inline const struct ipu6_cpd_ent *ipu6_cpd_get_entry(const void *cpd, #define ipu6_cpd_get_metadata(cpd) ipu6_cpd_get_entry(cpd, METADATA_IDX) #define ipu6_cpd_get_moduledata(cpd) ipu6_cpd_get_entry(cpd, MODULEDATA_IDX) +#define IPU7_CPD_BINARY_START_IDX 1U +#define IPU7_CPD_METADATA_START_IDX 2U +#define IPU7_CPD_BINARY_NUM 2U /* ISYS + PSYS */ +#define IPU7_CPD_METADATA_ATTR 0xa +#define IPU7_CPD_METADATA_IPL 0x1c +/* + * Entries include: + * 1 manifest entry. + * 1 metadata entry for each sub system(ISYS and PSYS). + * 1 binary entry for each sub system(ISYS and PSYS). + */ +#define IPU7_CPD_ENTRY_NUM (IPU7_CPD_BINARY_NUM * 2U + 1U) +#define IPU7_MAX_MANIFEST_SIZE (SZ_4K * sizeof(u32)) + static const struct ipu6_cpd_metadata_cmpnt_hdr * ipu6_cpd_metadata_get_cmpnt(struct ipu6_device *isp, const void *metadata, unsigned int metadata_size, u8 idx) @@ -220,8 +235,9 @@ EXPORT_SYMBOL_NS_GPL(ipu6_cpd_create_pkg_dir, "INTEL_IPU6"); void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev) { - ipu6_dma_free(adev, adev->pkg_dir_size, adev->pkg_dir, - adev->pkg_dir_dma_addr, 0); + if (adev->pkg_dir) + ipu6_dma_free(adev, adev->pkg_dir_size, adev->pkg_dir, + adev->pkg_dir_dma_addr, 0); } EXPORT_SYMBOL_NS_GPL(ipu6_cpd_free_pkg_dir, "INTEL_IPU6"); @@ -266,7 +282,6 @@ static int ipu6_cpd_validate_moduledata(struct ipu6_device *isp, u32 moduledata_size) { const struct ipu6_cpd_module_data_hdr *mod_hdr = moduledata; - int ret; /* Ensure moduledata hdr is within moduledata */ if (moduledata_size < sizeof(*mod_hdr) || @@ -276,15 +291,9 @@ static int ipu6_cpd_validate_moduledata(struct ipu6_device *isp, } dev_dbg(&isp->pdev->dev, "FW version: %x\n", mod_hdr->fw_pkg_date); - ret = ipu6_cpd_validate_cpd(isp, moduledata + mod_hdr->hdr_len, - moduledata_size - mod_hdr->hdr_len, - moduledata_size); - if (ret) { - dev_err(&isp->pdev->dev, "Invalid CPD in moduledata\n"); - return ret; - } - - return 0; + return ipu6_cpd_validate_cpd(isp, moduledata + mod_hdr->hdr_len, + moduledata_size - mod_hdr->hdr_len, + moduledata_size); } static int ipu6_cpd_validate_metadata(struct ipu6_device *isp, @@ -316,47 +325,163 @@ static int ipu6_cpd_validate_metadata(struct ipu6_device *isp, return 0; } -int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file, - unsigned long cpd_file_size) +static struct ipu7_cpd_metadata *ipu7_cpd_get_metadata(const void *cpd, int idx) { - const struct ipu6_cpd_hdr *hdr = cpd_file; - const struct ipu6_cpd_ent *ent; - int ret; + const struct ipu6_cpd_ent *cpd_ent = + ipu6_cpd_get_entry(cpd, IPU7_CPD_METADATA_START_IDX + idx * 2); - ret = ipu6_cpd_validate_cpd(isp, cpd_file, cpd_file_size, - cpd_file_size); - if (ret) { - dev_err(&isp->pdev->dev, "Invalid CPD in file\n"); - return ret; + return (struct ipu7_cpd_metadata *)((u8 *)cpd + cpd_ent->offset); +} + +static int ipu7_cpd_validate_metadata(struct ipu6_device *isp, + const void *cpd, int idx) +{ + const struct ipu6_cpd_ent *cpd_ent = + ipu6_cpd_get_entry(cpd, IPU7_CPD_METADATA_START_IDX + idx * 2); + const struct ipu6_cpd_ent *bin_ent = + ipu6_cpd_get_entry(cpd, IPU7_CPD_BINARY_START_IDX + idx * 2); + const struct ipu7_cpd_metadata *metadata = + ipu7_cpd_get_metadata(cpd, idx); + struct device *dev = &isp->pdev->dev; + u32 offset; + + /* Sanity check for metadata size */ + if (cpd_ent->len != sizeof(struct ipu7_cpd_metadata)) { + dev_err(dev, "Invalid metadata size\n"); + return -EINVAL; } - /* Check for CPD file marker */ - if (hdr->hdr_mark != CPD_HDR_MARK) { - dev_err(&isp->pdev->dev, "Invalid CPD header\n"); + /* Validate type and length of metadata sections */ + if (metadata->attr.hdr.type != IPU7_CPD_METADATA_ATTR) { + dev_err(dev, "Invalid metadata attr type (%d)\n", + metadata->attr.hdr.type); + return -EINVAL; + } + if (metadata->attr.hdr.len != sizeof(struct ipu7_cpd_metadata_attr)) { + dev_err(dev, "Invalid metadata attr size (%d)\n", + metadata->attr.hdr.len); + return -EINVAL; + } + if (metadata->ipl.hdr.type != IPU7_CPD_METADATA_IPL) { + dev_err(dev, "Invalid metadata ipl type (%d)\n", + metadata->ipl.hdr.type); + return -EINVAL; + } + if (metadata->ipl.hdr.len != sizeof(struct ipu7_cpd_metadata_ipl)) { + dev_err(dev, "Invalid metadata ipl size (%d)\n", + metadata->ipl.hdr.len); + return -EINVAL; + } + + offset = metadata->ipl.param[0]; + if (offset > IPU7_FW_CODE_REGION_SIZE || + bin_ent->len > IPU7_FW_CODE_REGION_SIZE - offset) { + dev_err(dev, "Incorrect binary size %u and offset %u\n", + bin_ent->len, offset); return -EINVAL; } - /* Sanity check for manifest size */ + return 0; +} + +static int __ipu7_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file, + unsigned long cpd_file_size) +{ + const struct ipu6_cpd_ent *ent; + const struct ipu7_cpd_hdr *hdr = cpd_file; + unsigned int i; + ent = ipu6_cpd_get_manifest(cpd_file); - if (ent->len > MAX_MANIFEST_SIZE) { + if (ent->len > IPU7_MAX_MANIFEST_SIZE) { dev_err(&isp->pdev->dev, "Invalid CPD manifest size\n"); return -EINVAL; } + /* Sanity check for CPD entry header */ + if (hdr->ent_cnt != IPU7_CPD_ENTRY_NUM) { + dev_err(&isp->pdev->dev, "Invalid CPD entry number %d\n", + hdr->ent_cnt); + return -EINVAL; + } /* Validate metadata */ + for (i = 0; i < IPU7_CPD_BINARY_NUM; i++) { + int ret = ipu7_cpd_validate_metadata(isp, cpd_file, i); + + if (ret) { + dev_err(&isp->pdev->dev, "Invalid metadata(%d)\n", i); + return ret; + } + } + return 0; +} + +static int __ipu6_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file, + unsigned long cpd_file_size) +{ + const struct ipu6_cpd_ent *ent; + int ret; + + ent = ipu6_cpd_get_manifest(cpd_file); + if (ent->len > MAX_MANIFEST_SIZE) { + dev_err(&isp->pdev->dev, "Invalid CPD manifest size\n"); + return -EINVAL; + } + ent = ipu6_cpd_get_metadata(cpd_file); ret = ipu6_cpd_validate_metadata(isp, cpd_file + ent->offset, ent->len); - if (ret) { - dev_err(&isp->pdev->dev, "Invalid CPD metadata\n"); + if (ret) return ret; - } - /* Validate moduledata */ ent = ipu6_cpd_get_moduledata(cpd_file); - ret = ipu6_cpd_validate_moduledata(isp, cpd_file + ent->offset, - ent->len); + return ipu6_cpd_validate_moduledata(isp, cpd_file + ent->offset, + ent->len); +} + +int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file, + unsigned long cpd_file_size) +{ + const struct ipu6_cpd_hdr *hdr = cpd_file; + int ret; + + ret = ipu6_cpd_validate_cpd(isp, cpd_file, cpd_file_size, + cpd_file_size); if (ret) - dev_err(&isp->pdev->dev, "Invalid CPD moduledata\n"); + return ret; + + /* Check for CPD file marker */ + if (hdr->hdr_mark != CPD_HDR_MARK) { + dev_err(&isp->pdev->dev, "Invalid CPD header\n"); + return -EINVAL; + } + + if (IS_IPU7(isp)) + return __ipu7_validate_cpd_file(isp, cpd_file, cpd_file_size); + + return __ipu6_validate_cpd_file(isp, cpd_file, cpd_file_size); +} + +int ipu6_ipu7_cpd_copy_binary(const void *cpd, const char *name, void *dst, + u32 *entry) +{ + unsigned int i; + + for (i = 0; i < IPU7_CPD_BINARY_NUM; i++) { + const struct ipu7_cpd_metadata *metadata; + u8 idx = IPU7_CPD_BINARY_START_IDX + i * 2U; + const struct ipu6_cpd_ent *ent = + ipu6_cpd_get_entry(cpd, idx); + + if (strncmp(ent->name, name, sizeof(ent->name))) + continue; + + metadata = ipu7_cpd_get_metadata(cpd, i); + memcpy(dst + metadata->ipl.param[0], cpd + ent->offset, + ent->len); + *entry = metadata->ipl.param[2]; + + return 0; + } - return ret; + return -ENOENT; } +EXPORT_SYMBOL_NS_GPL(ipu6_ipu7_cpd_copy_binary, "INTEL_IPU6"); diff --git a/drivers/media/pci/intel/ipu6/ipu6-cpd.h b/drivers/media/pci/intel/ipu6/ipu6-cpd.h index e0e4fdeca902..c3a4859d8e79 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-cpd.h +++ b/drivers/media/pci/intel/ipu6/ipu6-cpd.h @@ -98,8 +98,51 @@ struct ipu6_cpd_client_pkg_hdr { u32 prog_bin_size; } __packed; +/* IPU7 */ + +struct ipu7_cpd_hdr { + u32 hdr_mark; + u32 ent_cnt; + u8 hdr_ver; + u8 ent_ver; + u8 hdr_len; + u8 rsvd; + u8 partition_name[4]; + u32 crc32; +} __packed; + +struct ipu7_cpd_metadata_hdr { + u32 type; + u32 len; +} __packed; + +struct ipu7_cpd_metadata_attr { + struct ipu7_cpd_metadata_hdr hdr; + u8 compression_type; + u8 encryption_type; + u8 rsvd[2]; + u32 uncompressed_size; + u32 compressed_size; + u32 module_id; + u8 hash[48]; +} __packed; + +struct ipu7_cpd_metadata_ipl { + struct ipu7_cpd_metadata_hdr hdr; + u32 param[4]; + u8 rsvd[8]; +} __packed; + +struct ipu7_cpd_metadata { + struct ipu7_cpd_metadata_attr attr; + struct ipu7_cpd_metadata_ipl ipl; +} __packed; + int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src); void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev); int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file, unsigned long cpd_file_size); +int ipu6_ipu7_cpd_copy_binary(const void *cpd, const char *name, void *dst, + u32 *entry); + #endif /* IPU6_CPD_H */ diff --git a/drivers/media/pci/intel/ipu6/ipu6-dma.c b/drivers/media/pci/intel/ipu6/ipu6-dma.c index fdcdb15b073c..90f530a93779 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-dma.c +++ b/drivers/media/pci/intel/ipu6/ipu6-dma.c @@ -286,7 +286,7 @@ void ipu6_dma_free(struct ipu6_bus_device *sys, size_t size, void *vaddr, __free_buffer(pages, size, attrs); - mmu->tlb_invalidate(mmu); + mmu->ops->tlb_invalidate(mmu); __free_iova(&mmu->dmap->iovad, iova); @@ -366,11 +366,22 @@ void ipu6_dma_unmap_sg(struct ipu6_bus_device *sys, struct scatterlist *sglist, ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), PFN_PHYS(iova_size(iova))); - mmu->tlb_invalidate(mmu); + mmu->ops->tlb_invalidate(mmu); __free_iova(&mmu->dmap->iovad, iova); } EXPORT_SYMBOL_NS_GPL(ipu6_dma_unmap_sg, "INTEL_IPU6"); +static struct iova *ipu7_get_fw_code_region(struct ipu6_bus_device *sys) +{ + struct ipu6_mmu *mmu = sys->mmu; + unsigned long lo, hi; + + lo = iova_pfn(&mmu->dmap->iovad, IPU7_FW_CODE_REGION_START); + hi = iova_pfn(&mmu->dmap->iovad, IPU7_FW_CODE_REGION_END) - 1U; + + return reserve_iova(&mmu->dmap->iovad, lo, hi); +} + int ipu6_dma_map_sg(struct ipu6_bus_device *sys, struct scatterlist *sglist, int nents, enum dma_data_direction dir, unsigned long attrs) @@ -397,10 +408,13 @@ int ipu6_dma_map_sg(struct ipu6_bus_device *sys, struct scatterlist *sglist, dev_dbg(dev, "dmamap trying to map %d ents %zu pages\n", nents, npages); - iova = alloc_iova(&mmu->dmap->iovad, npages, - PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0); + if (attrs & DMA_ATTR_RESERVE_REGION) + iova = ipu7_get_fw_code_region(sys); + else + iova = alloc_iova(&mmu->dmap->iovad, npages, + PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0); if (!iova) - return 0; + return -ENOMEM; dev_dbg(dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo, iova->pfn_hi); diff --git a/drivers/media/pci/intel/ipu6/ipu6-dma.h b/drivers/media/pci/intel/ipu6/ipu6-dma.h index ae9b9a5df57f..e5e0e7860423 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-dma.h +++ b/drivers/media/pci/intel/ipu6/ipu6-dma.h @@ -10,6 +10,8 @@ #include "ipu6-bus.h" +#define DMA_ATTR_RESERVE_REGION BIT(31) + struct ipu6_mmu_info; struct ipu6_dma_mapping { diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c index 62ed92ff1d30..f4f1cf7c86d2 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c +++ b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c @@ -7,6 +7,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/io.h> +#include <linux/pm_runtime.h> #include <linux/spinlock.h> #include <linux/types.h> @@ -15,6 +16,7 @@ #include "ipu6-isys.h" #include "ipu6-platform-isys-csi2-reg.h" #include "ipu6-platform-regs.h" +#include "ipu7-fw-isys.h" static const char send_msg_types[N_IPU6_FW_ISYS_SEND_TYPE][32] = { "STREAM_OPEN", @@ -32,7 +34,7 @@ static int handle_proxy_response(struct ipu6_isys *isys, unsigned int req_id) struct ipu6_fw_isys_proxy_resp_info_abi *resp; int ret; - resp = ipu6_recv_get_token(isys->fwcom, IPU6_BASE_PROXY_RECV_QUEUES); + resp = ipu6_recv_get_token(isys->fwctx, IPU6_BASE_PROXY_RECV_QUEUES); if (!resp) return 1; @@ -42,7 +44,7 @@ static int handle_proxy_response(struct ipu6_isys *isys, unsigned int req_id) ret = req_id == resp->request_id ? 0 : -EIO; - ipu6_recv_put_token(isys->fwcom, IPU6_BASE_PROXY_RECV_QUEUES); + ipu6_recv_put_token(isys->fwctx, IPU6_BASE_PROXY_RECV_QUEUES); return ret; } @@ -52,7 +54,7 @@ int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys, unsigned int index, unsigned int offset, u32 value) { - struct ipu6_fw_com_context *ctx = isys->fwcom; + struct ipu6_fw_com_context *ctx = isys->fwctx; struct device *dev = &isys->adev->auxdev.dev; struct ipu6_fw_proxy_send_queue_token *token; unsigned int timeout = 1000; @@ -90,13 +92,13 @@ int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys, return ret; } -int ipu6_fw_isys_complex_cmd(struct ipu6_isys *isys, - const unsigned int stream_handle, - void *cpu_mapped_buf, - dma_addr_t dma_mapped_buf, - size_t size, u16 send_type) +static int ipu6_fw_isys_complex_cmd(struct ipu6_isys *isys, + const unsigned int stream_handle, + void *cpu_mapped_buf, + dma_addr_t dma_mapped_buf, + size_t size, u16 send_type) { - struct ipu6_fw_com_context *ctx = isys->fwcom; + struct ipu6_fw_com_context *ctx = isys->fwctx; struct device *dev = &isys->adev->auxdev.dev; struct ipu6_fw_send_queue_token *token; @@ -126,19 +128,11 @@ int ipu6_fw_isys_complex_cmd(struct ipu6_isys *isys, return 0; } -int ipu6_fw_isys_simple_cmd(struct ipu6_isys *isys, - const unsigned int stream_handle, u16 send_type) -{ - return ipu6_fw_isys_complex_cmd(isys, stream_handle, NULL, 0, 0, - send_type); -} - -int ipu6_fw_isys_close(struct ipu6_isys *isys) +static int ipu6_fw_isys_close(struct ipu6_isys *isys) { struct device *dev = &isys->adev->auxdev.dev; int retry = IPU6_ISYS_CLOSE_RETRY; - unsigned long flags; - void *fwcom; + void *fwctx; int ret; /* @@ -147,40 +141,36 @@ int ipu6_fw_isys_close(struct ipu6_isys *isys) * to SP icache. * spinlock to wait the interrupt handler to be finished */ - spin_lock_irqsave(&isys->power_lock, flags); - ret = ipu6_fw_com_close(isys->fwcom); - fwcom = isys->fwcom; - isys->fwcom = NULL; - spin_unlock_irqrestore(&isys->power_lock, flags); + ret = ipu6_fw_com_close(isys->fwctx); + fwctx = isys->fwctx; + isys->fwctx = NULL; if (ret) dev_err(dev, "Device close failure: %d\n", ret); /* release probably fails if the close failed. Let's try still */ do { usleep_range(400, 500); - ret = ipu6_fw_com_release(fwcom, 0); + ret = ipu6_fw_com_release(fwctx, 0); retry--; } while (ret && retry); if (ret) { dev_err(dev, "Device release time out %d\n", ret); - spin_lock_irqsave(&isys->power_lock, flags); - isys->fwcom = fwcom; - spin_unlock_irqrestore(&isys->power_lock, flags); + isys->fwctx = fwctx; } return ret; } -void ipu6_fw_isys_cleanup(struct ipu6_isys *isys) +static void ipu6_fw_isys_cleanup(struct ipu6_isys *isys) { int ret; - ret = ipu6_fw_com_release(isys->fwcom, 1); + ret = ipu6_fw_com_release(isys->fwctx, 1); if (ret < 0) dev_warn(&isys->adev->auxdev.dev, "Device busy, fw_com release failed."); - isys->fwcom = NULL; + isys->fwctx = NULL; } static void start_sp(struct ipu6_bus_device *adev) @@ -212,7 +202,7 @@ static int query_sp(struct ipu6_bus_device *adev) } static int ipu6_isys_fwcom_cfg_init(struct ipu6_isys *isys, - struct ipu6_fw_com_cfg *fwcom, + struct ipu6_fw_com_cfg *fwcom_cfg, unsigned int num_streams) { unsigned int max_send_queues, max_sram_blocks, max_devq_size; @@ -258,14 +248,16 @@ static int ipu6_isys_fwcom_cfg_init(struct ipu6_isys *isys, if (!output_queue_cfg) return -ENOMEM; - fwcom->input = input_queue_cfg; - fwcom->output = output_queue_cfg; + fwcom_cfg->input = input_queue_cfg; + fwcom_cfg->output = output_queue_cfg; - fwcom->num_input_queues = isys_fw_cfg->num_send_queues[type_proxy] + + fwcom_cfg->num_input_queues = + isys_fw_cfg->num_send_queues[type_proxy] + isys_fw_cfg->num_send_queues[type_dev] + isys_fw_cfg->num_send_queues[type_msg]; - fwcom->num_output_queues = isys_fw_cfg->num_recv_queues[type_proxy] + + fwcom_cfg->num_output_queues = + isys_fw_cfg->num_recv_queues[type_proxy] + isys_fw_cfg->num_recv_queues[type_dev] + isys_fw_cfg->num_recv_queues[type_msg]; @@ -280,7 +272,7 @@ static int ipu6_isys_fwcom_cfg_init(struct ipu6_isys *isys, isys_fw_cfg->buffer_partition.num_gda_pages[i] = 0; } - /* FW assumes proxy interface at fwcom queue 0 */ + /* FW assumes proxy interface at fwcom_cfg queue 0 */ for (i = 0; i < isys_fw_cfg->num_send_queues[type_proxy]; i++) { input_queue_cfg[i].token_size = sizeof(struct ipu6_fw_proxy_send_queue_token); @@ -314,34 +306,34 @@ static int ipu6_isys_fwcom_cfg_init(struct ipu6_isys *isys, IPU6_ISYS_SIZE_RECV_QUEUE; } - fwcom->dmem_addr = isys->pdata->ipdata->hw_variant.dmem_offset; - fwcom->specific_addr = isys_fw_cfg; - fwcom->specific_size = sizeof(*isys_fw_cfg); + fwcom_cfg->dmem_addr = isys->pdata->ipdata->hw_variant.dmem_offset; + fwcom_cfg->specific_addr = isys_fw_cfg; + fwcom_cfg->specific_size = sizeof(*isys_fw_cfg); return 0; } -int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams) +static int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams) { struct device *dev = &isys->adev->auxdev.dev; int retry = IPU6_ISYS_OPEN_RETRY; - struct ipu6_fw_com_cfg fwcom = { + struct ipu6_fw_com_cfg fwcom_cfg = { .cell_start = start_sp, .cell_ready = query_sp, .buttress_boot_offset = SYSCOM_BUTTRESS_FW_PARAMS_ISYS_OFFSET, }; int ret; - ipu6_isys_fwcom_cfg_init(isys, &fwcom, num_streams); + ipu6_isys_fwcom_cfg_init(isys, &fwcom_cfg, num_streams); - isys->fwcom = ipu6_fw_com_prepare(&fwcom, isys->adev, + isys->fwctx = ipu6_fw_com_prepare(&fwcom_cfg, isys->adev, isys->pdata->base); - if (!isys->fwcom) { + if (!isys->fwctx) { dev_err(dev, "isys fw com prepare failed\n"); return -EIO; } - ret = ipu6_fw_com_open(isys->fwcom); + ret = ipu6_fw_com_open(isys->fwctx); if (ret) { dev_err(dev, "isys fw com open failed %d\n", ret); return ret; @@ -349,7 +341,7 @@ int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams) do { usleep_range(400, 500); - if (ipu6_fw_com_ready(isys->fwcom)) + if (ipu6_fw_com_ready(isys->fwctx)) break; retry--; } while (retry > 0); @@ -363,20 +355,21 @@ int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams) return ret; } -struct ipu6_fw_isys_resp_info_abi * -ipu6_fw_isys_get_resp(void *context, unsigned int queue) +static struct ipu6_fw_isys_resp_info_abi * +ipu6_fw_isys_get_resp(struct ipu6_isys *isys) { - return ipu6_recv_get_token(context, queue); + return ipu6_recv_get_token(isys->fwctx, IPU6_BASE_MSG_RECV_QUEUES); } -void ipu6_fw_isys_put_resp(void *context, unsigned int queue) +static void ipu6_fw_isys_put_resp(struct ipu6_isys *isys) { - ipu6_recv_put_token(context, queue); + ipu6_recv_put_token(isys->fwctx, IPU6_BASE_MSG_RECV_QUEUES); } -void ipu6_fw_isys_dump_stream_cfg(struct device *dev, - struct ipu6_fw_isys_stream_cfg_data_abi *cfg) +static void ipu6_fw_isys_dump_stream_cfg(struct device *dev, + struct isys_fw_msgs *msg) { + struct ipu6_fw_isys_stream_cfg_data_abi *cfg = &msg->ipu6.stream; unsigned int i; dev_dbg(dev, "-----------------------------------------------------\n"); @@ -451,13 +444,15 @@ void ipu6_fw_isys_dump_stream_cfg(struct device *dev, dev_dbg(dev, "-----------------------------------------------------\n"); } -void -ipu6_fw_isys_dump_frame_buff_set(struct device *dev, - struct ipu6_fw_isys_frame_buff_set_abi *buf, - unsigned int outputs) +static void +ipu6_fw_isys_dump_frame_buf_set(struct device *dev, struct isys_fw_msgs *msg, + unsigned int outputs) { + struct ipu6_fw_isys_frame_buff_set_abi *buf; unsigned int i; + buf = &msg->ipu6.frame; + dev_dbg(dev, "-----------------------------------------------------\n"); dev_dbg(dev, "IPU6_FW_ISYS_FRAME_BUFF_SET\n"); @@ -485,3 +480,478 @@ ipu6_fw_isys_dump_frame_buff_set(struct device *dev, dev_dbg(dev, "-----------------------------------------------------\n"); } + +struct fwmsg { + int type; + char *msg; + bool valid_ts; +}; + +static const struct fwmsg fw_msg[] = { + {IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE, "STREAM_OPEN_DONE", 0}, + {IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK, "STREAM_CLOSE_ACK", 0}, + {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK, "STREAM_START_ACK", 0}, + {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK, + "STREAM_START_AND_CAPTURE_ACK", 0}, + {IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK, "STREAM_STOP_ACK", 0}, + {IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK, "STREAM_FLUSH_ACK", 0}, + {IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY, "PIN_DATA_READY", 1}, + {IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK, "STREAM_CAPTURE_ACK", 0}, + {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE, + "STREAM_START_AND_CAPTURE_DONE", 1}, + {IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE, "STREAM_CAPTURE_DONE", 1}, + {IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF, "FRAME_SOF", 1}, + {IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF, "FRAME_EOF", 1}, + {IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY, "STATS_READY", 1}, + {-1, "UNKNOWN MESSAGE", 0} +}; + +static u32 resp_type_to_index(int type) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(fw_msg); i++) + if (fw_msg[i].type == type) + return i; + + return ARRAY_SIZE(fw_msg) - 1; +} + +int ipu6_isys_isr_one(struct ipu6_bus_device *adev) +{ + struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); + struct ipu6_fw_isys_resp_info_abi *resp; + struct ipu6_isys_stream *stream; + struct ipu6_isys_csi2 *csi2 = NULL; + struct isys_fw_msgs *isys_fw_msg = NULL; + u32 index; + u64 ts; + + resp = ipu6_fw_isys_get_resp(isys); + if (!resp) + return 1; + + ts = (u64)resp->timestamp[1] << 32 | resp->timestamp[0]; + + index = resp_type_to_index(resp->type); + dev_dbg(&adev->auxdev.dev, + "FW resp %02d %s, stream %u, ts 0x%16.16llx, pin %d\n", + resp->type, fw_msg[index].msg, resp->stream_handle, + fw_msg[index].valid_ts ? ts : 0, resp->pin_id); + + if (resp->error_info.error == IPU6_FW_ISYS_ERROR_STREAM_IN_SUSPENSION) + /* Suspension is kind of special case: not enough buffers */ + dev_dbg(&adev->auxdev.dev, + "FW error resp SUSPENSION, details %d\n", + resp->error_info.error_details); + else if (resp->error_info.error) + dev_dbg(&adev->auxdev.dev, + "FW error resp error %d, details %d\n", + resp->error_info.error, resp->error_info.error_details); + + guard(spinlock_irqsave)(&isys->streams_lock); + + if (resp->stream_handle >= IPU6_ISYS_MAX_STREAMS) { + dev_err(&adev->auxdev.dev, "bad stream handle %u\n", + resp->stream_handle); + goto leave; + } + + stream = resp->stream_handle < IPU6_ISYS_MAX_STREAMS ? + isys->streams_by_handle[resp->stream_handle] : NULL; + if (!stream) { + dev_err(&adev->auxdev.dev, "stream of stream_handle %u is unused\n", + resp->stream_handle); + goto leave; + } + stream->error = resp->error_info.error; + + csi2 = ipu6_isys_subdev_to_csi2(stream->asd); + + switch (resp->type) { + case IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE: + complete(&stream->stream_open_completion); + break; + case IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK: + complete(&stream->stream_close_completion); + break; + case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK: + complete(&stream->stream_start_completion); + break; + case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK: + complete(&stream->stream_start_completion); + break; + case IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK: + complete(&stream->stream_stop_completion); + break; + case IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK: + complete(&stream->stream_stop_completion); + break; + case IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY: + /* + * firmware only release the capture message until software + * get pin_data_ready event + */ + if (!resp->buf_id) { + dev_warn(&adev->auxdev.dev, "%d: Invalid buf ID\n", + resp->stream_handle); + goto leave; + } + + isys_fw_msg = container_of((void *)(uintptr_t)resp->buf_id, + struct isys_fw_msgs, dummy); + + ipu6_put_fw_msg_buf(ipu6_bus_get_drvdata(adev), isys_fw_msg); + if (resp->pin_id < IPU6_ISYS_OUTPUT_PINS && + stream->output_pins_queue[resp->pin_id]) + ipu6_isys_queue_buf_ready(stream, resp); + else + dev_warn(&adev->auxdev.dev, + "%d:No queue for pin id %d\n", + resp->stream_handle, resp->pin_id); + if (csi2) + ipu6_isys_csi2_error(csi2); + + break; + case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK: + break; + case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE: + case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE: + break; + case IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF: + + ipu6_isys_csi2_sof_event_by_stream(stream); + stream->seq[stream->seq_index].sequence = + atomic_read(&stream->sequence) - 1; + stream->seq[stream->seq_index].timestamp = ts; + dev_dbg(&adev->auxdev.dev, + "sof: handle %d: (index %u), timestamp 0x%16.16llx\n", + resp->stream_handle, + stream->seq[stream->seq_index].sequence, ts); + stream->seq_index = (stream->seq_index + 1) + % IPU6_ISYS_MAX_PARALLEL_SOF; + break; + case IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF: + ipu6_isys_csi2_eof_event_by_stream(stream); + dev_dbg(&adev->auxdev.dev, + "eof: handle %d: (index %u), timestamp 0x%16.16llx\n", + resp->stream_handle, + stream->seq[stream->seq_index].sequence, ts); + break; + case IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY: + break; + default: + dev_err(&adev->auxdev.dev, "%d:unknown response type %u\n", + resp->stream_handle, resp->type); + break; + } + +leave: + ipu6_fw_isys_put_resp(isys); + return 0; +} + +static void ipu6_isys_csi2_isr(struct ipu6_isys_csi2 *csi2) +{ + struct ipu6_isys_stream *stream; + unsigned int i; + u32 status; + + ipu6_isys_register_errors(csi2); + + status = readl(csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + + CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET); + + writel(status, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + + CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET); + + scoped_guard(spinlock, &csi2->isys->streams_lock) { + for (i = 0; i < NR_OF_CSI2_VC; i++) { + if (status & IPU_CSI_RX_IRQ_FS_VC(i)) { + stream = csi2->streams_by_vc[i]; + if (!stream) + continue; + + ipu6_isys_csi2_sof_event_by_stream(stream); + } + + if (status & IPU_CSI_RX_IRQ_FE_VC(i)) { + stream = csi2->streams_by_vc[i]; + if (!stream) + continue; + + ipu6_isys_csi2_eof_event_by_stream(stream); + } + } + } +} + +irqreturn_t ipu6_isys_isr(struct ipu6_bus_device *adev) +{ + struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); + void __iomem *base = isys->pdata->base; + u32 status_sw, status_csi; + u32 ctrl0_status, ctrl0_clear; + int pm_status; + + pm_status = pm_runtime_get_if_active(&adev->auxdev.dev); + if (!pm_status) + return 0; + + ctrl0_status = isys->pdata->ipdata->csi2.ctrl0_irq_status; + ctrl0_clear = isys->pdata->ipdata->csi2.ctrl0_irq_clear; + + status_csi = readl(isys->pdata->base + ctrl0_status); + status_sw = readl(isys->pdata->base + + IPU6_REG_ISYS_UNISPART_IRQ_STATUS); + + writel(ISYS_UNISPART_IRQS & ~IPU6_ISYS_UNISPART_IRQ_SW, + base + IPU6_REG_ISYS_UNISPART_IRQ_MASK); + + do { + writel(status_csi, isys->pdata->base + ctrl0_clear); + + writel(status_sw, isys->pdata->base + + IPU6_REG_ISYS_UNISPART_IRQ_CLEAR); + + if (isys->isr_csi2_bits & status_csi) { + unsigned int i; + + for (i = 0; i < isys->pdata->ipdata->csi2.nports; i++) { + /* irq from not enabled port */ + if (!isys->csi2[i].base) + continue; + if (status_csi & IPU6_ISYS_UNISPART_IRQ_CSI2(i)) + ipu6_isys_csi2_isr(&isys->csi2[i]); + } + } + + writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_REG); + + if (!ipu6_isys_isr_one(adev)) + status_sw = IPU6_ISYS_UNISPART_IRQ_SW; + else + status_sw = 0; + + status_csi = readl(isys->pdata->base + ctrl0_status); + status_sw |= readl(isys->pdata->base + + IPU6_REG_ISYS_UNISPART_IRQ_STATUS); + } while ((status_csi & isys->isr_csi2_bits) || + (status_sw & IPU6_ISYS_UNISPART_IRQ_SW)); + + writel(ISYS_UNISPART_IRQS, base + IPU6_REG_ISYS_UNISPART_IRQ_MASK); + + if (pm_status > 0) + pm_runtime_put(&adev->auxdev.dev); + + return IRQ_HANDLED; +} + +static int ipu6_isys_fw_pin_cfg(struct ipu6_isys_video *av, + struct ipu6_isys_stream *stream, + struct media_pad *src_pad, + struct v4l2_mbus_frame_desc_entry *entry, + void *__cfg) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity); + struct v4l2_subdev_state *state = v4l2_subdev_get_locked_active_state(sd); + struct ipu6_fw_isys_stream_cfg_data_abi *cfg = __cfg; + struct ipu6_fw_isys_input_pin_info_abi *input_pin; + struct ipu6_fw_isys_output_pin_info_abi *output_pin; + struct ipu6_isys_queue *aq = &av->aq; + struct v4l2_mbus_framefmt *fmt; + const struct ipu6_isys_pixelformat *pfmt = + ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0); + struct v4l2_rect v4l2_crop; + struct ipu6_isys *isys = av->isys; + int input_pins = cfg->nof_input_pins++; + int output_pins; + u32 src_stream; + + src_stream = __ipu6_isys_get_src_stream_by_src_pad(state, src_pad->index); + fmt = v4l2_subdev_state_get_format(state, src_pad->index, src_stream); + v4l2_crop = *v4l2_subdev_state_get_crop(state, src_pad->index, src_stream); + + input_pin = &cfg->input_pins[input_pins]; + input_pin->input_res.width = fmt->width; + input_pin->input_res.height = fmt->height; + input_pin->dt = entry->bus.csi2.dt; + input_pin->bits_per_pix = pfmt->bpp_packed; + input_pin->mapped_dt = 0x40; /* invalid mipi data type */ + input_pin->mipi_decompression = 0; + input_pin->capture_mode = IPU6_FW_ISYS_CAPTURE_MODE_REGULAR; + input_pin->mipi_store_mode = pfmt->bpp == pfmt->bpp_packed ? + IPU6_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER : + IPU6_FW_ISYS_MIPI_STORE_MODE_NORMAL; + input_pin->crop_first_and_last_lines = v4l2_crop.top & 1; + + output_pins = cfg->nof_output_pins++; + aq->fw_output = output_pins; + stream->output_pins_queue[output_pins] = aq; + + output_pin = &cfg->output_pins[output_pins]; + output_pin->input_pin_id = input_pins; + output_pin->output_res.width = ipu6_isys_get_frame_width(av); + output_pin->output_res.height = ipu6_isys_get_frame_height(av); + + output_pin->stride = ipu6_isys_get_bytes_per_line(av); + if (pfmt->bpp != pfmt->bpp_packed) + output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_RAW_SOC; + else + output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_MIPI; + output_pin->ft = pfmt->css_pixelformat; + output_pin->send_irq = 1; + memset(output_pin->ts_offsets, 0, sizeof(output_pin->ts_offsets)); + output_pin->s2m_pixel_soc_pixel_remapping = + S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING; + output_pin->csi_be_soc_pixel_remapping = + CSI_BE_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING; + + output_pin->snoopable = true; + output_pin->error_handling_enable = false; + output_pin->sensor_type = isys->sensor_type++; + if (isys->sensor_type > isys->pdata->ipdata->sensor_type_end) + isys->sensor_type = isys->pdata->ipdata->sensor_type_start; + + return 0; +} + +static int ipu6_fw_isys_prepare_stream_cfg(struct ipu6_isys_stream *stream, + struct v4l2_mbus_frame_desc *desc, + struct isys_fw_msgs *msg) +{ + struct ipu6_fw_isys_stream_cfg_data_abi *stream_cfg; + struct device *dev = &stream->isys->adev->auxdev.dev; + int ret; + + stream_cfg = &msg->ipu6.stream; + stream_cfg->src = stream->asd->source; + stream_cfg->vc = stream->vc; + stream_cfg->isl_use = 0; + stream_cfg->sensor_type = IPU6_FW_ISYS_SENSOR_MODE_NORMAL; + + ret = ipu6_isys_fw_pins_prepare(stream, desc, ipu6_isys_fw_pin_cfg, + stream_cfg); + if (ret) + return ret; + + ipu6_fw_isys_dump_stream_cfg(dev, msg); + + stream->nr_output_pins = stream_cfg->nof_output_pins; + + return 0; +} + +static int ipu6_fw_isys_stream_open(struct ipu6_isys *isys, + const unsigned int stream_handle, + struct isys_fw_msgs *msg) +{ + return ipu6_fw_isys_complex_cmd(isys, stream_handle, + &msg->ipu6.stream, msg->dma_addr, + sizeof(msg->ipu6.stream), + IPU6_FW_ISYS_SEND_TYPE_STREAM_OPEN); +} + +static int ipu6_fw_isys_stream_close(struct ipu6_isys *isys, + const unsigned int stream_handle) +{ + return ipu6_fw_isys_complex_cmd(isys, stream_handle, NULL, 0, 0, + IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE); +} + +static int ipu6_fw_isys_stream_flush(struct ipu6_isys *isys, + const unsigned int stream_handle) +{ + return ipu6_fw_isys_complex_cmd(isys, stream_handle, NULL, 0, 0, + IPU6_FW_ISYS_SEND_TYPE_STREAM_FLUSH); +} + +static int ipu6_fw_isys_stream_start(struct ipu6_isys *isys, + const unsigned int stream_handle, + struct isys_fw_msgs *msg, bool capture) +{ + u16 cmd_type; + + if (capture) + cmd_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE; + else + cmd_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START; + + return ipu6_fw_isys_complex_cmd(isys, stream_handle, + &msg->ipu6.stream, msg->dma_addr, + sizeof(msg->ipu6.stream), cmd_type); +} + +static int ipu6_fw_isys_stream_capture(struct ipu6_isys *isys, + const unsigned int stream_handle, + struct isys_fw_msgs *msg) +{ + return ipu6_fw_isys_complex_cmd(isys, stream_handle, + &msg->ipu6.stream, msg->dma_addr, + sizeof(msg->ipu6.stream), + IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE); +} + +static void +ipu6_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb, + struct ipu6_fw_isys_frame_buff_set_abi *set) +{ + struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); + struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); + struct ipu6_isys_video_buffer *ivb = + vb2_buffer_to_ipu6_isys_video_buffer(vvb); + + set->output_pins[aq->fw_output].addr = ivb->dma_addr; + set->output_pins[aq->fw_output].out_buf_id = vb->index + 1; +} + +/* + * Convert a buffer list to a isys fw ABI framebuffer set. The + * buffer list is not modified. + */ +#define IPU6_ISYS_FRAME_NUM_THRESHOLD (30) +static void ipu6_fw_isys_prepare_buf_set(struct isys_fw_msgs *msg, + struct ipu6_isys_stream *stream, + struct ipu6_isys_buffer_list *bl) +{ + struct ipu6_fw_isys_frame_buff_set_abi *set = &msg->ipu6.frame; + struct ipu6_isys_buffer *ib; + + WARN_ON(!bl->nbufs); + + set->send_irq_sof = 1; + set->send_resp_sof = 1; + set->send_irq_eof = 0; + set->send_resp_eof = 0; + + set->send_irq_capture_ack = 1; + set->send_irq_capture_done = 0; + + set->send_resp_capture_ack = 1; + set->send_resp_capture_done = 1; + if (atomic_read(&stream->sequence) >= IPU6_ISYS_FRAME_NUM_THRESHOLD) { + set->send_resp_capture_ack = 0; + set->send_resp_capture_done = 0; + } + + list_for_each_entry(ib, &bl->head, head) { + struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib); + + ipu6_isys_buf_to_fw_frame_buf_pin(vb, set); + } +} + +const struct ipu6_fw_isys_ops ipu6_fw_isys_ops = { + .init = ipu6_fw_isys_init, + .close = ipu6_fw_isys_close, + .cleanup = ipu6_fw_isys_cleanup, + .prepare_stream_cfg = ipu6_fw_isys_prepare_stream_cfg, + .prepare_buf_set = ipu6_fw_isys_prepare_buf_set, + .stream_open = ipu6_fw_isys_stream_open, + .stream_start = ipu6_fw_isys_stream_start, + .stream_capture = ipu6_fw_isys_stream_capture, + .stream_flush = ipu6_fw_isys_stream_flush, + .stream_close = ipu6_fw_isys_stream_close, + .dump_stream_cfg = ipu6_fw_isys_dump_stream_cfg, + .dump_frame_buf_set = ipu6_fw_isys_dump_frame_buf_set, +}; diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h index b60f02076d8a..2a679d995961 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h +++ b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h @@ -8,6 +8,10 @@ struct device; struct ipu6_isys; +struct ipu6_isys_video; +struct isys_fw_msgs; +struct ipu6_isys_stream; +struct ipu6_isys_buffer_list; /* Max number of Input/Output Pins */ #define IPU6_MAX_IPINS 4 @@ -512,21 +516,6 @@ struct ipu6_fw_isys_proxy_resp_info_abi { }; /** - * struct ipu6_fw_proxy_write_queue_token - ISYS proxy write queue token - * @request_id: update id for the specific proxy write request - * @region_index: Region id for the proxy write request - * @offset: Offset of the write request according to the base address - * of the region - * @value: Value that is requested to be written with the proxy write request - */ -struct ipu6_fw_proxy_write_queue_token { - u32 request_id; - u32 region_index; - u32 offset; - u32 value; -}; - -/** * struct ipu6_fw_resp_queue_token - ISYS response queue token * @resp_info: response info */ @@ -570,27 +559,10 @@ struct ipu6_fw_proxy_send_queue_token { u32 value; }; -void -ipu6_fw_isys_dump_stream_cfg(struct device *dev, - struct ipu6_fw_isys_stream_cfg_data_abi *cfg); -void -ipu6_fw_isys_dump_frame_buff_set(struct device *dev, - struct ipu6_fw_isys_frame_buff_set_abi *buf, - unsigned int outputs); -int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams); -int ipu6_fw_isys_close(struct ipu6_isys *isys); -int ipu6_fw_isys_simple_cmd(struct ipu6_isys *isys, - const unsigned int stream_handle, u16 send_type); -int ipu6_fw_isys_complex_cmd(struct ipu6_isys *isys, - const unsigned int stream_handle, - void *cpu_mapped_buf, dma_addr_t dma_mapped_buf, - size_t size, u16 send_type); -int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys, - unsigned int req_id, - unsigned int index, - unsigned int offset, u32 value); -void ipu6_fw_isys_cleanup(struct ipu6_isys *isys); -struct ipu6_fw_isys_resp_info_abi * -ipu6_fw_isys_get_resp(void *context, unsigned int queue); -void ipu6_fw_isys_put_resp(void *context, unsigned int queue); +int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys, unsigned int req_id, + unsigned int index, unsigned int offset, + u32 value); +int ipu6_isys_isr_one(struct ipu6_bus_device *adev); +irqreturn_t ipu6_isys_isr(struct ipu6_bus_device *adev); + #endif diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c index 7e539a0c6c92..0db6971d8d3e 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c @@ -11,9 +11,12 @@ #include <linux/err.h> #include <linux/io.h> #include <linux/minmax.h> +#include <linux/pm_runtime.h> #include <linux/sprintf.h> +#include <linux/string_choices.h> #include <media/media-entity.h> +#include <media/mipi-csi2.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-event.h> @@ -23,7 +26,9 @@ #include "ipu6-isys.h" #include "ipu6-isys-csi2.h" #include "ipu6-isys-subdev.h" +#include "ipu6-isys-video.h" #include "ipu6-platform-isys-csi2-reg.h" +#include "ipu7-isys-csi2-regs.h" static const u32 csi2_supported_codes[] = { MEDIA_BUS_FMT_RGB565_1X16, @@ -228,56 +233,124 @@ void ipu6_isys_csi2_error(struct ipu6_isys_csi2 *csi2) } } -static int ipu6_isys_csi2_set_stream(struct v4l2_subdev *sd, - const struct ipu6_isys_csi2_timing *timing, - unsigned int nlanes, int enable) +static void ipu6_isys_csi2_setup_watermark(struct ipu6_isys_csi2 *csi2, + struct v4l2_subdev_state *csi2_state, + struct v4l2_subdev *remote_sd) +{ + struct device *dev = &csi2->isys->adev->auxdev.dev; + struct v4l2_control hb = { .id = V4L2_CID_HBLANK, .value = 0 }; + struct v4l2_subdev_route *route; + u32 max_stream_data_rate = 0, hblank = 0; + s64 link_freq; + int ret; + + if (IS_IPU7(csi2->asd.isys->adev->isp)) + return; + + ret = v4l2_g_ctrl(remote_sd->ctrl_handler, &hb); + if (!ret) + hblank = max(0, hb.value); + + link_freq = ipu6_isys_csi2_get_link_freq(csi2); + if (link_freq <= 0) { + csi2->watermark.force_iwake_disable = true; + dev_warn(dev, "unexpected link_freq %lld (source %s)\n", + link_freq, remote_sd->entity.name); + ipu6_isys_update_watermark_setting(csi2->isys); + return; + } + + for_each_active_route(&csi2_state->routing, route) { + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(csi2_state, CSI2_PAD_SINK, + route->sink_stream); + if (WARN_ON(!fmt)) + continue; + + u32 bpp = ipu6_isys_mbus_code_to_bpp(fmt->code); + u64 pixel_rate = mul_u64_u32_div(link_freq, csi2->nlanes * 2, + bpp); + u32 pixels_per_line = fmt->width + hblank; + u64 line_time_ns = div_u64(pixels_per_line * NSEC_PER_SEC, + pixel_rate); + u32 bytes_per_line = fmt->width * bpp / 8; + u32 pages_per_line = + DIV_ROUND_UP(bytes_per_line, + csi2->isys->pdata->ipdata->sram_gran_size); + u32 pb_bytes_per_line = + pages_per_line << csi2->isys->pdata->ipdata->sram_gran_shift; + u64 stream_data_rate = + div64_u64(pb_bytes_per_line * 1000, line_time_ns); + + dev_dbg(dev, "stream %u:%u -> %u:%u data rate %lld\n", + route->sink_pad, route->sink_stream, route->source_pad, + route->source_stream, stream_data_rate); + + max_stream_data_rate = max(max_stream_data_rate, + stream_data_rate); + } + + csi2->watermark.stream_data_rate = max_stream_data_rate; + + ipu6_isys_update_watermark_setting(csi2->isys); +} + +static void ipu6_isys_csi2_clear_watermark(struct ipu6_isys_csi2 *csi2) +{ + if (IS_IPU7(csi2->asd.isys->adev->isp)) + return; + + csi2->watermark.force_iwake_disable = false; + csi2->watermark.stream_data_rate = 0; + ipu6_isys_update_watermark_setting(csi2->isys); +} + +static void ipu6_isys_csi2_stream_disable(struct ipu6_isys_csi2 *csi2) { - struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); - struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd); struct ipu6_isys *isys = csi2->isys; - struct device *dev = &isys->adev->auxdev.dev; struct ipu6_isys_csi2_config cfg; - unsigned int nports; - int ret = 0; - u32 mask = 0; - u32 i; - - dev_dbg(dev, "stream %s CSI2-%u with %u lanes\n", enable ? "on" : "off", - csi2->port, nlanes); + u32 mask = isys->pdata->ipdata->csi2.irq_mask; cfg.port = csi2->port; - cfg.nlanes = nlanes; + cfg.nlanes = csi2->nlanes; - mask = isys->pdata->ipdata->csi2.irq_mask; - nports = isys->pdata->ipdata->csi2.nports; + writel(0, csi2->base + CSI_REG_CSI_FE_ENABLE); + writel(0, csi2->base + CSI_REG_PPI2CSI_ENABLE); + writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + + CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET); + writel(mask, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + + CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET); + writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + + CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET); + writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + + CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET); - if (!enable) { - writel(0, csi2->base + CSI_REG_CSI_FE_ENABLE); - writel(0, csi2->base + CSI_REG_PPI2CSI_ENABLE); - - writel(0, - csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + - CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET); - writel(mask, - csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + - CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET); - writel(0, - csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + - CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET); - writel(0xffffffff, - csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + - CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET); - - isys->phy_set_power(isys, &cfg, timing, false); - - writel(0, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT - (isys->pdata->ipdata->csi2.fw_access_port_ofs, - csi2->port)); - writel(0, isys->pdata->base + - CSI_REG_HUB_DRV_ACCESS_PORT(csi2->port)); + isys->phy_set_power(isys, &cfg, NULL, false); + writel(0, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT + (isys->pdata->ipdata->csi2.fw_access_port_ofs, csi2->port)); + writel(0, isys->pdata->base + CSI_REG_HUB_DRV_ACCESS_PORT(csi2->port)); +} + +static int ipu6_isys_csi2_stream_enable(struct ipu6_isys_csi2 *csi2) +{ + struct ipu6_isys *isys = csi2->isys; + struct ipu6_isys_csi2_timing timing = { }; + struct ipu6_isys_csi2_config cfg; + unsigned int nports; + u32 mask; + int ret; + + cfg.port = csi2->port; + cfg.nlanes = csi2->nlanes; + + ret = ipu6_isys_csi2_calc_timing(csi2, &timing, CSI2_ACCINV); + if (ret) return ret; - } + + mask = isys->pdata->ipdata->csi2.irq_mask; + nports = isys->pdata->ipdata->csi2.nports; /* reset port reset */ writel(0x1, csi2->base + CSI_REG_PORT_GPREG_SRST); @@ -285,7 +358,7 @@ static int ipu6_isys_csi2_set_stream(struct v4l2_subdev *sd, writel(0x0, csi2->base + CSI_REG_PORT_GPREG_SRST); /* enable port clock */ - for (i = 0; i < nports; i++) { + for (unsigned int i = 0; i < nports; i++) { writel(1, isys->pdata->base + CSI_REG_HUB_DRV_ACCESS_PORT(i)); writel(1, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT (isys->pdata->ipdata->csi2.fw_access_port_ofs, i)); @@ -329,18 +402,217 @@ static int ipu6_isys_csi2_set_stream(struct v4l2_subdev *sd, writel(CSI_SENSOR_INPUT, csi2->base + CSI_REG_CSI_FE_MUX_CTRL); writel(CSI_CNTR_SENSOR_LINE_ID | CSI_CNTR_SENSOR_FRAME_ID, csi2->base + CSI_REG_CSI_FE_SYNC_CNTR_SEL); - writel(FIELD_PREP(PPI_INTF_CONFIG_NOF_ENABLED_DLANES_MASK, nlanes - 1), + writel(FIELD_PREP(PPI_INTF_CONFIG_NOF_ENABLED_DLANES_MASK, + csi2->nlanes - 1), csi2->base + CSI_REG_PPI2CSI_CONFIG_PPI_INTF); writel(1, csi2->base + CSI_REG_PPI2CSI_ENABLE); writel(1, csi2->base + CSI_REG_CSI_FE_ENABLE); - ret = isys->phy_set_power(isys, &cfg, timing, true); + return isys->phy_set_power(isys, &cfg, &timing, true); +} + +static void ipu7_csi2_irq_enable(struct ipu6_isys_csi2 *csi2) +{ + struct ipu6_isys *isys = csi2->isys; + unsigned int offset, mask; + + /* enable CSI2 legacy error irq */ + offset = IPU7_IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port); + mask = IPU7_CSI_RX_ERROR_IRQ_MASK; + writel(mask, csi2->base + offset + IPU7_IRQ_CTL_CLEAR); + writel(mask, csi2->base + offset + IPU7_IRQ_CTL_MASK); + writel(mask, csi2->base + offset + IPU7_IRQ_CTL_ENABLE); + + /* enable CSI2 legacy sync irq */ + offset = IPU7_IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port); + mask = IPU7_CSI_RX_SYNC_IRQ_MASK; + writel(mask, csi2->base + offset + IPU7_IRQ_CTL_CLEAR); + writel(mask, csi2->base + offset + IPU7_IRQ_CTL_MASK); + writel(mask, csi2->base + offset + IPU7_IRQ_CTL_ENABLE); + + if (!IS_IPU7_MTL(isys->adev->isp)) { + mask = IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK; + writel(mask, csi2->base + offset + IPU7_IRQ1_CTL_CLEAR); + writel(mask, csi2->base + offset + IPU7_IRQ1_CTL_MASK); + writel(mask, csi2->base + offset + IPU7_IRQ1_CTL_ENABLE); + } +} + +static void ipu7_csi2_irq_disable(struct ipu6_isys_csi2 *csi2) +{ + struct ipu6_isys *isys = csi2->isys; + unsigned int offset, mask; + + /* disable CSI2 legacy error irq */ + offset = IPU7_IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port); + mask = IPU7_CSI_RX_ERROR_IRQ_MASK; + writel(mask, csi2->base + offset + IPU7_IRQ_CTL_CLEAR); + writel(0, csi2->base + offset + IPU7_IRQ_CTL_MASK); + writel(0, csi2->base + offset + IPU7_IRQ_CTL_ENABLE); + + /* disable CSI2 legacy sync irq */ + offset = IPU7_IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port); + mask = IPU7_CSI_RX_SYNC_IRQ_MASK; + writel(mask, csi2->base + offset + IPU7_IRQ_CTL_CLEAR); + writel(0, csi2->base + offset + IPU7_IRQ_CTL_MASK); + writel(0, csi2->base + offset + IPU7_IRQ_CTL_ENABLE); + + if (!IS_IPU7_MTL(isys->adev->isp)) { + writel(mask, csi2->base + offset + IPU7_IRQ1_CTL_CLEAR); + writel(0, csi2->base + offset + IPU7_IRQ1_CTL_MASK); + writel(0, csi2->base + offset + IPU7_IRQ1_CTL_ENABLE); + } +} + +static void ipu7_isys_csi2_stream_disable(struct ipu6_isys_csi2 *csi2) +{ + struct ipu6_isys *isys = csi2->isys; + void __iomem *base = isys->pdata->base; + struct ipu6_isys_csi2_config cfg; + + cfg.port = csi2->port; + cfg.nlanes = csi2->nlanes; + + isys->phy_set_power(isys, &cfg, NULL, false); + + writel(0x4, + base + IPU7_IS_IO_GPREGS_BASE + IPU7_CLK_DIV_FACTOR_APB_CLK); + ipu7_csi2_irq_disable(csi2); +} + +static int ipu7_isys_csi2_stream_enable(struct ipu6_isys_csi2 *csi2) +{ + struct ipu6_isys *isys = csi2->isys; + void __iomem *base = isys->pdata->base; + struct ipu6_isys_csi2_config cfg; + unsigned int offset = IPU7_IS_IO_GPREGS_BASE; + int ret; + + writel(0x2, base + offset + IPU7_CLK_DIV_FACTOR_APB_CLK); + if (csi2->port == 0U && csi2->nlanes == 4U && + !IS_IPU7_MTL(isys->adev->isp)) + writel(0x1, base + offset + IPU7_CSI_PORTAB_AGGREGATION); + + /* input is coming from CSI receiver (sensor) */ + offset = IPU7_IS_IO_CSI2_ADPL_PORT_BASE(csi2->port); + writel(CSI_SENSOR_INPUT, base + offset + IPU7_CSI2_ADPL_INPUT_MODE); + writel(1, base + offset + IPU7_CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN); + + cfg.port = csi2->port; + cfg.nlanes = csi2->nlanes; + + ret = isys->phy_set_power(isys, &cfg, NULL, true); if (ret) - dev_err(dev, "csi-%d phy power up failed %d\n", csi2->port, - ret); + return ret; - return ret; + ipu7_csi2_irq_enable(csi2); + + return 0; +} + +static int ipu6_isys_csi2_streaming_change(struct ipu6_isys_subdev *asd, + struct v4l2_subdev_state *state, + u32 pad, + struct v4l2_mbus_frame_desc *desc, + u8 *vc, bool enable) +{ + struct v4l2_mbus_frame_desc_entry *this_entry = NULL; + struct v4l2_subdev_route *route, *this_route = NULL; + u32 streams_enabled = 0, nodes_streaming = 0; + + for_each_active_route(&state->routing, this_route) + if (pad == this_route->source_pad) + break; + if (!this_route) { + dev_dbg(asd->sd.dev, "no route found for pad %u\n", pad); + return -EINVAL; + } + + for (unsigned int i = 0; i < desc->num_entries; i++) { + if (desc->entry[i].stream == this_route->sink_stream) { + this_entry = &desc->entry[i]; + break; + } + } + if (!this_entry) { + dev_dbg(asd->sd.dev, + "no frame descriptor entry found for stream %u\n", + this_route->sink_stream); + return -EINVAL; + } + + for_each_active_route(&state->routing, route) { + struct v4l2_mbus_frame_desc_entry *entry = NULL; + + for (unsigned int i = 0; i < desc->num_entries; i++) { + if (desc->entry[i].stream == route->sink_stream) { + entry = &desc->entry[i]; + break; + } + } + + if (!entry) { + dev_dbg(asd->sd.dev, "cannot find stream %u from frame descriptor\n", + route->sink_stream); + return -EINVAL; + } + + if (entry->bus.csi2.vc != this_entry->bus.csi2.vc) + continue; + + struct media_pad *video_pad = + media_pad_remote_pad_first(&asd->sd.entity.pads[route->source_pad]); + if (!video_pad) + return -EINVAL; + + struct ipu6_isys_video *av = + container_of_const(video_pad, struct ipu6_isys_video, + pad); + + streams_enabled++; + if (av->streaming || (enable && pad == route->source_pad)) + nodes_streaming++; + } + + if (vc) + *vc = this_entry->bus.csi2.vc; + + if (streams_enabled == nodes_streaming) { + dev_dbg(asd->sd.dev, + "changing streaming state to %s on \"%s\"\n", + str_enabled_disabled(enable), asd->sd.entity.name); + return 1; + } + + dev_dbg(asd->sd.dev, "not setting streaming %s %s (%u/%u)\n", + str_enabled_disabled(enable), enable ? "yet" : "anymore", + nodes_streaming, streams_enabled); + + return 0; +} + +static int ipu6_isys_get_frame_desc(struct v4l2_subdev *remote_sd, + unsigned int pad, + struct v4l2_mbus_frame_desc *desc) +{ + struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_ACTIVE }; + int ret; + + ret = v4l2_subdev_call(remote_sd, pad, get_frame_desc, pad, desc); + if (!ret || ret != -ENOIOCTLCMD) + return ret; + + ret = v4l2_subdev_call_state_active(remote_sd, pad, get_fmt, &fmt); + if (ret) + return ret; + + desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + desc->num_entries = 1; + desc->entry[0].pixelcode = fmt.format.code; + desc->entry[0].bus.csi2.dt = ipu6_isys_mbus_code_to_mipi(fmt.format.code); + + return 0; } static int ipu6_isys_csi2_enable_streams(struct v4l2_subdev *sd, @@ -349,60 +621,174 @@ static int ipu6_isys_csi2_enable_streams(struct v4l2_subdev *sd, { struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd); - struct ipu6_isys_csi2_timing timing = { }; - struct v4l2_subdev *remote_sd; - struct media_pad *remote_pad; + struct ipu6_device *isp = asd->isys->adev->isp; + struct media_pad *remote_pad = + media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]), + *vdev_pad = media_pad_remote_pad_unique(&sd->entity.pads[pad]); + struct ipu6_isys_video *av = + container_of_const(vdev_pad, struct ipu6_isys_video, pad); + struct v4l2_subdev *remote_sd = + media_entity_to_v4l2_subdev(remote_pad->entity); + struct v4l2_mbus_frame_desc desc = { 0 }; + struct ipu6_isys_stream *stream; + struct ipu6_isys_buffer_list bl; u64 sink_streams; int ret; + u8 vc; - remote_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]); - remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + lockdep_assert_held(&csi2->isys->stream_mutex); + + ret = ipu6_isys_get_frame_desc(remote_sd, remote_pad->index, &desc); + if (ret) + return ret; + + list_add(&av->csi2_entry, &csi2->av_head); sink_streams = v4l2_subdev_state_xlate_streams(state, pad, CSI2_PAD_SINK, &streams_mask); + csi2->stream_ids |= sink_streams; - ret = ipu6_isys_csi2_calc_timing(csi2, &timing, CSI2_ACCINV); - if (ret) + ret = ipu6_isys_csi2_streaming_change(asd, state, pad, &desc, &vc, + true); + if (!ret) return ret; + if (ret < 0) + goto err_av_del; + + ret = pm_runtime_resume_and_get(sd->dev); + if (ret < 0) + goto err_del_av; - ret = ipu6_isys_csi2_set_stream(sd, &timing, csi2->nlanes, true); + ipu6_isys_csi2_setup_watermark(csi2, state, remote_sd); + + stream = ipu6_isys_alloc_stream_firmware(csi2, state, &desc, vc); + if (IS_ERR(stream)) { + ret = PTR_ERR(stream); + dev_err(sd->dev, "allocating firmware stream failed\n"); + goto err_clear_watermark; + } + + ret = ipu6_isys_buffer_list_get(stream, &bl); + if (ret < 0) { + dev_warn(sd->dev, "no buffer available, DRIVER BUG?\n"); + goto err_free_stream_firmware; + } + + ret = ipu6_isys_start_stream_firmware(stream, &bl, &desc); if (ret) - return ret; + goto err_requeue_buffers; - ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index, - sink_streams); - if (ret) { - ipu6_isys_csi2_set_stream(sd, NULL, 0, false); - return ret; + if (!csi2->streaming_vc) { + ret = IS_IPU7(isp) ? ipu7_isys_csi2_stream_enable(csi2) : + ipu6_isys_csi2_stream_enable(csi2); + if (ret) + goto err_stop_stream_firmware; } + ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index, + csi2->stream_ids); + if (ret) + goto err_stop_stream_csi2; + + csi2->streaming_vc |= BIT(vc); + return 0; + +err_stop_stream_csi2: + if (IS_IPU7(isp)) + ipu7_isys_csi2_stream_disable(csi2); + else + ipu6_isys_csi2_stream_disable(csi2); + +err_requeue_buffers: + ipu6_isys_buffer_list_queue(&bl, IPU6_ISYS_BUFFER_LIST_FL_INCOMING, 0); + +err_stop_stream_firmware: + ipu6_isys_stop_stream_firmware(stream); + ipu6_isys_close_stream_firmware(stream); + +err_free_stream_firmware: + ipu6_isys_free_stream_firmware(stream); + +err_clear_watermark: + ipu6_isys_csi2_clear_watermark(csi2); + pm_runtime_put(sd->dev); + +err_del_av: + ipu6_isys_csi2_streaming_change(asd, state, pad, &desc, NULL, false); + csi2->stream_ids &= ~sink_streams; +err_av_del: + list_del(&av->csi2_entry); + + return ret; } static int ipu6_isys_csi2_disable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, u32 pad, u64 streams_mask) { - struct v4l2_subdev *remote_sd; - struct media_pad *remote_pad; + struct media_pad *remote_pad = + media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]), + *vdev_pad = media_pad_remote_pad_unique(&sd->entity.pads[pad]); + struct ipu6_isys_video *av = + container_of_const(vdev_pad, struct ipu6_isys_video, pad); + struct v4l2_subdev *remote_sd = + media_entity_to_v4l2_subdev(remote_pad->entity); + struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); + struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd); + struct ipu6_device *isp = asd->isys->adev->isp; + struct v4l2_mbus_frame_desc desc = { 0 }; u64 sink_streams; + int ret; + u8 vc; + + lockdep_assert_held(&csi2->isys->stream_mutex); + + ret = ipu6_isys_get_frame_desc(remote_sd, remote_pad->index, &desc); + if (ret) + return ret; sink_streams = v4l2_subdev_state_xlate_streams(state, pad, CSI2_PAD_SINK, &streams_mask); - remote_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]); - remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + csi2->stream_ids &= ~sink_streams; + + ret = ipu6_isys_csi2_streaming_change(asd, state, pad, &desc, &vc, + false); + if (ret <= 0) + goto out_del_csi2_entry; + + csi2->streaming_vc &= ~BIT(vc); + + struct ipu6_isys_stream *stream = + ipu6_isys_find_stream_firmware(csi2, vc); + ipu6_isys_stop_stream_firmware(stream); - ipu6_isys_csi2_set_stream(sd, NULL, 0, false); + if IS_IPU7(isp) + ipu7_isys_csi2_stream_disable(csi2); + else + ipu6_isys_csi2_stream_disable(csi2); - v4l2_subdev_disable_streams(remote_sd, remote_pad->index, sink_streams); + v4l2_subdev_disable_streams(remote_sd, remote_pad->index, + csi2->stream_ids | sink_streams); + + ipu6_isys_close_stream_firmware(stream); + ipu6_isys_free_stream_firmware(stream); + + ipu6_isys_csi2_clear_watermark(csi2); + + pm_runtime_put(sd->dev); + +out_del_csi2_entry: + list_del(&av->csi2_entry); return 0; } static int ipu6_isys_csi2_set_sel(struct v4l2_subdev *sd, + const struct v4l2_subdev_client_info *ci, struct v4l2_subdev_state *state, struct v4l2_subdev_selection *sel) { @@ -454,6 +840,7 @@ static int ipu6_isys_csi2_set_sel(struct v4l2_subdev *sd, } static int ipu6_isys_csi2_get_sel(struct v4l2_subdev *sd, + const struct v4l2_subdev_client_info *ci, struct v4l2_subdev_state *state, struct v4l2_subdev_selection *sel) { @@ -542,6 +929,8 @@ int ipu6_isys_csi2_init(struct ipu6_isys_csi2 *csi2, if (ret) goto fail; + INIT_LIST_HEAD(&csi2->av_head); + INIT_LIST_HEAD(&csi2->streams); csi2->asd.source = IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT0 + index; csi2->asd.supported_codes = csi2_supported_codes; snprintf(csi2->asd.sd.name, sizeof(csi2->asd.sd.name), @@ -592,56 +981,3 @@ void ipu6_isys_csi2_eof_event_by_stream(struct ipu6_isys_stream *stream) dev_dbg(dev, "eof_event::csi2-%i sequence: %i\n", csi2->port, frame_sequence); } - -int ipu6_isys_csi2_get_remote_desc(u32 source_stream, - struct ipu6_isys_csi2 *csi2, - struct media_entity *source_entity, - struct v4l2_mbus_frame_desc_entry *entry) -{ - struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; - struct device *dev = &csi2->isys->adev->auxdev.dev; - struct v4l2_mbus_frame_desc desc; - struct v4l2_subdev *source; - struct media_pad *pad; - unsigned int i; - int ret; - - source = media_entity_to_v4l2_subdev(source_entity); - if (!source) - return -EPIPE; - - pad = media_pad_remote_pad_first(&csi2->asd.pad[CSI2_PAD_SINK]); - if (!pad) - return -EPIPE; - - ret = v4l2_subdev_call(source, pad, get_frame_desc, pad->index, &desc); - if (ret) - return ret; - - if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { - dev_err(dev, "Unsupported frame descriptor type\n"); - return -EINVAL; - } - - for (i = 0; i < desc.num_entries; i++) { - if (source_stream == desc.entry[i].stream) { - desc_entry = &desc.entry[i]; - break; - } - } - - if (!desc_entry) { - dev_err(dev, "Failed to find stream %u from remote subdev\n", - source_stream); - return -EINVAL; - } - - if (desc_entry->bus.csi2.vc >= NR_OF_CSI2_VC) { - dev_err(dev, "invalid vc %d\n", desc_entry->bus.csi2.vc); - return -EINVAL; - } - - *entry = *desc_entry; - - return 0; -} diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h index ce8eed91065c..1620ac16f90d 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h @@ -16,6 +16,9 @@ struct ipu6_isys_video; struct ipu6_isys; struct ipu6_isys_stream; +#define PHY_MODE_DPHY 0 +#define PHY_MODE_CPHY 1 + #define NR_OF_CSI2_VC 16 #define INVALID_VC_ID -1 #define NR_OF_CSI2_SINK_PADS 1 @@ -38,11 +41,22 @@ struct ipu6_isys_csi2 { struct ipu6_isys_subdev asd; struct ipu6_isys *isys; struct ipu6_isys_video av[NR_OF_CSI2_SRC_PADS]; + struct list_head av_head; + struct list_head streams; + struct ipu6_isys_stream *streams_by_vc[NR_OF_CSI2_VC]; void __iomem *base; u32 receiver_errors; unsigned int nlanes; unsigned int port; + u32 legacy_irq_mask; + unsigned int phy_mode; + struct { + u32 stream_data_rate; + bool force_iwake_disable; + } watermark; + u32 streaming_vc; + u64 stream_ids; }; struct ipu6_isys_csi2_timing { @@ -70,9 +84,5 @@ void ipu6_isys_csi2_sof_event_by_stream(struct ipu6_isys_stream *stream); void ipu6_isys_csi2_eof_event_by_stream(struct ipu6_isys_stream *stream); void ipu6_isys_register_errors(struct ipu6_isys_csi2 *csi2); void ipu6_isys_csi2_error(struct ipu6_isys_csi2 *csi2); -int ipu6_isys_csi2_get_remote_desc(u32 source_stream, - struct ipu6_isys_csi2 *csi2, - struct media_entity *source_entity, - struct v4l2_mbus_frame_desc_entry *entry); #endif /* IPU6_ISYS_CSI2_H */ diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c index fabaed63df0c..2608e3b91acb 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c @@ -21,6 +21,7 @@ #include "ipu6-dma.h" #include "ipu6-fw-isys.h" #include "ipu6-isys.h" +#include "ipu6-isys-queue.h" #include "ipu6-isys-video.h" static int ipu6_isys_buf_init(struct vb2_buffer *vb) @@ -191,8 +192,8 @@ static void flush_firmware_streamon_fail(struct ipu6_isys_stream *stream) * that contains one entry from each video buffer queue. If a buffer can't be * obtained from every queue, the buffers are returned back to the queue. */ -static int buffer_list_get(struct ipu6_isys_stream *stream, - struct ipu6_isys_buffer_list *bl) +int ipu6_isys_buffer_list_get(struct ipu6_isys_stream *stream, + struct ipu6_isys_buffer_list *bl) { struct device *dev = &stream->isys->adev->auxdev.dev; struct ipu6_isys_queue *aq; @@ -233,106 +234,53 @@ static int buffer_list_get(struct ipu6_isys_stream *stream, return 0; } -static void -ipu6_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb, - struct ipu6_fw_isys_frame_buff_set_abi *set) -{ - struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); - struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); - struct ipu6_isys_video_buffer *ivb = - vb2_buffer_to_ipu6_isys_video_buffer(vvb); - - set->output_pins[aq->fw_output].addr = ivb->dma_addr; - set->output_pins[aq->fw_output].out_buf_id = vb->index + 1; -} - -/* - * Convert a buffer list to a isys fw ABI framebuffer set. The - * buffer list is not modified. - */ -#define IPU6_ISYS_FRAME_NUM_THRESHOLD (30) -void -ipu6_isys_buf_to_fw_frame_buf(struct ipu6_fw_isys_frame_buff_set_abi *set, - struct ipu6_isys_stream *stream, - struct ipu6_isys_buffer_list *bl) -{ - struct ipu6_isys_buffer *ib; - - WARN_ON(!bl->nbufs); - - set->send_irq_sof = 1; - set->send_resp_sof = 1; - set->send_irq_eof = 0; - set->send_resp_eof = 0; - - if (stream->streaming) - set->send_irq_capture_ack = 0; - else - set->send_irq_capture_ack = 1; - set->send_irq_capture_done = 0; - - set->send_resp_capture_ack = 1; - set->send_resp_capture_done = 1; - if (atomic_read(&stream->sequence) >= IPU6_ISYS_FRAME_NUM_THRESHOLD) { - set->send_resp_capture_ack = 0; - set->send_resp_capture_done = 0; - } - - list_for_each_entry(ib, &bl->head, head) { - struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib); - - ipu6_isys_buf_to_fw_frame_buf_pin(vb, set); - } -} - /* Start streaming for real. The buffer list must be available. */ -static int ipu6_isys_stream_start(struct ipu6_isys_video *av, - struct ipu6_isys_buffer_list *bl) +static int ipu6_isys_stream_start(struct ipu6_isys_video *av) { - struct ipu6_isys_stream *stream = av->stream; - struct device *dev = &stream->isys->adev->auxdev.dev; - struct ipu6_isys_buffer_list __bl; + struct ipu6_bus_device *adev = av->isys->adev; + const struct ipu6_fw_isys_ops *fw_ops = adev->auxdrv_data->fw_ops; + struct device *dev = &adev->auxdev.dev; + struct ipu6_isys_buffer_list bl; + struct isys_fw_msgs *msg; int ret; - guard(mutex)(&stream->isys->stream_mutex); - ret = ipu6_isys_video_set_streaming(av, 1, bl); + guard(mutex)(&av->isys->stream_mutex); + ret = ipu6_isys_video_set_streaming(av, 1); if (ret) - goto out_requeue; + return ret; - stream->streaming = 1; + if (!av->stream || !(BIT(av->stream->vc) & av->csi2->streaming_vc)) + return 0; + + struct ipu6_isys_stream *stream = av->stream; - bl = &__bl; + guard(mutex)(&stream->mutex); do { - struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL; - struct isys_fw_msgs *msg; - u16 send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE; - - ret = buffer_list_get(stream, bl); + ret = ipu6_isys_buffer_list_get(stream, &bl); if (ret < 0) - break; + return 0; msg = ipu6_get_fw_msg_buf(stream); - if (!msg) - return -ENOMEM; - - buf = &msg->fw_msg.frame; - ipu6_isys_buf_to_fw_frame_buf(buf, stream, bl); - ipu6_fw_isys_dump_frame_buff_set(dev, buf, - stream->nr_output_pins); - ipu6_isys_buffer_list_queue(bl, IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, - 0); - ret = ipu6_fw_isys_complex_cmd(stream->isys, - stream->stream_handle, buf, - msg->dma_addr, sizeof(*buf), - send_type); - } while (!WARN_ON(ret)); + if (WARN_ON(!msg)) + goto out_requeue; + + fw_ops->prepare_buf_set(msg, stream, &bl); + fw_ops->dump_frame_buf_set(dev, msg, stream->nr_output_pins); + ipu6_isys_buffer_list_queue(&bl, + IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0); + ret = fw_ops->stream_capture(stream->isys, + stream->stream_handle, msg); + if (WARN_ON(ret)) + break; + } while (true); - return 0; + /* Error handling begins here. */ + ipu6_put_fw_msg_buf(stream->isys, msg); out_requeue: - if (bl && bl->nbufs) - ipu6_isys_buffer_list_queue(bl, + if (bl.nbufs) + ipu6_isys_buffer_list_queue(&bl, IPU6_ISYS_BUFFER_LIST_FL_INCOMING, VB2_BUF_STATE_QUEUED); flush_firmware_streamon_fail(stream); @@ -344,13 +292,15 @@ static void buf_queue(struct vb2_buffer *vb) { struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); + struct ipu6_bus_device *adev = av->isys->adev; + const struct ipu6_fw_isys_ops *fw_ops = adev->auxdrv_data->fw_ops; struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); struct ipu6_isys_video_buffer *ivb = vb2_buffer_to_ipu6_isys_video_buffer(vvb); struct ipu6_isys_buffer *ib = &ivb->ib; - struct device *dev = &av->isys->adev->auxdev.dev; - struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL; + struct device *dev = &adev->auxdev.dev; struct ipu6_isys_stream *stream = av->stream; + struct ipu6_isys_csi2 *csi2; struct ipu6_isys_buffer_list bl; struct isys_fw_msgs *msg; unsigned long flags; @@ -374,7 +324,8 @@ static void buf_queue(struct vb2_buffer *vb) mutex_lock(&stream->mutex); - if (stream->nr_streaming != stream->nr_queues) { + csi2 = ipu6_isys_subdev_to_csi2(stream->asd); + if (!(BIT(stream->vc) & csi2->streaming_vc)) { dev_dbg(dev, "not streaming yet, adding to incoming\n"); goto out; } @@ -384,7 +335,7 @@ static void buf_queue(struct vb2_buffer *vb) * (above). Let's see whether all queues in the pipeline would * have a buffer. */ - ret = buffer_list_get(stream, &bl); + ret = ipu6_isys_buffer_list_get(stream, &bl); if (ret < 0) { dev_dbg(dev, "No buffers available\n"); goto out; @@ -396,9 +347,8 @@ static void buf_queue(struct vb2_buffer *vb) goto out; } - buf = &msg->fw_msg.frame; - ipu6_isys_buf_to_fw_frame_buf(buf, stream, &bl); - ipu6_fw_isys_dump_frame_buff_set(dev, buf, stream->nr_output_pins); + fw_ops->prepare_buf_set(msg, stream, &bl); + fw_ops->dump_frame_buf_set(dev, msg, stream->nr_output_pins); /* * We must queue the buffers in the buffer list to the @@ -408,9 +358,7 @@ static void buf_queue(struct vb2_buffer *vb) */ ipu6_isys_buffer_list_queue(&bl, IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0); - ret = ipu6_fw_isys_complex_cmd(stream->isys, stream->stream_handle, - buf, msg->dma_addr, sizeof(*buf), - IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE); + ret = fw_ops->stream_capture(stream->isys, stream->stream_handle, msg); if (ret < 0) dev_err(dev, "send stream capture failed\n"); @@ -525,7 +473,6 @@ static void return_buffers(struct ipu6_isys_queue *aq, static void ipu6_isys_stream_cleanup(struct ipu6_isys_video *av) { video_device_pipeline_stop(&av->vdev); - ipu6_isys_put_stream(av->stream); av->stream = NULL; } @@ -536,10 +483,8 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) struct device *dev = &av->isys->adev->auxdev.dev; const struct ipu6_isys_pixelformat *pfmt = ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0); - struct ipu6_isys_buffer_list __bl, *bl = NULL; - struct ipu6_isys_stream *stream; struct media_pad *source_pad, *remote_pad; - int nr_queues, ret; + int ret; dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n", av->vdev.name, ipu6_isys_get_frame_width(av), @@ -559,11 +504,9 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) goto out_return_buffers; } - ret = ipu6_isys_setup_video(av, remote_pad, source_pad, &nr_queues); - if (ret < 0) { - dev_dbg(dev, "failed to setup video\n"); + ret = video_device_pipeline_alloc_start(&av->vdev); + if (ret < 0) goto out_return_buffers; - } ret = ipu6_isys_link_fmt_validate(aq); if (ret) { @@ -573,55 +516,12 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) goto out_pipeline_stop; } - ret = ipu6_isys_fw_open(av->isys); + ret = ipu6_isys_stream_start(av); if (ret) goto out_pipeline_stop; - stream = av->stream; - mutex_lock(&stream->mutex); - if (!stream->nr_streaming) { - ret = ipu6_isys_video_prepare_stream(av, source_pad->entity, - nr_queues); - if (ret) - goto out_fw_close; - } - - stream->nr_streaming++; - dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming, - stream->nr_queues); - - list_add(&aq->node, &stream->queues); - ipu6_isys_configure_stream_watermark(av, source_pad->entity); - ipu6_isys_update_stream_watermark(av, true); - - if (stream->nr_streaming != stream->nr_queues) - goto out; - - bl = &__bl; - ret = buffer_list_get(stream, bl); - if (ret < 0) { - dev_warn(dev, "no buffer available, DRIVER BUG?\n"); - goto out; - } - - ret = ipu6_isys_stream_start(av, bl); - if (ret) - goto out_stream_start; - -out: - mutex_unlock(&stream->mutex); - return 0; -out_stream_start: - ipu6_isys_update_stream_watermark(av, false); - list_del(&aq->node); - stream->nr_streaming--; - -out_fw_close: - mutex_unlock(&stream->mutex); - ipu6_isys_fw_close(av->isys); - out_pipeline_stop: ipu6_isys_stream_cleanup(av); @@ -635,27 +535,14 @@ static void stop_streaming(struct vb2_queue *q) { struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(q); struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); - struct ipu6_isys_stream *stream = av->stream; - - mutex_lock(&stream->mutex); - - ipu6_isys_update_stream_watermark(av, false); mutex_lock(&av->isys->stream_mutex); - if (stream->nr_streaming == stream->nr_queues && stream->streaming) - ipu6_isys_video_set_streaming(av, 0, NULL); - list_del(&aq->node); + ipu6_isys_video_set_streaming(av, 0); mutex_unlock(&av->isys->stream_mutex); - stream->nr_streaming--; - stream->streaming = 0; - mutex_unlock(&stream->mutex); - ipu6_isys_stream_cleanup(av); return_buffers(aq, VB2_BUF_STATE_ERROR); - - ipu6_isys_fw_close(av->isys); } static unsigned int @@ -741,7 +628,7 @@ static void ipu6_isys_queue_buf_done(struct ipu6_isys_buffer *ib) } } -static void +void ipu6_stream_buf_ready(struct ipu6_isys_stream *stream, u8 pin_id, u32 pin_addr, u64 time, bool error_check) { diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h index dec1fed44dd2..0e0886f59150 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h @@ -60,11 +60,11 @@ struct ipu6_isys_buffer_list { void ipu6_isys_buffer_list_queue(struct ipu6_isys_buffer_list *bl, unsigned long op_flags, enum vb2_buffer_state state); -void -ipu6_isys_buf_to_fw_frame_buf(struct ipu6_fw_isys_frame_buff_set_abi *set, - struct ipu6_isys_stream *stream, +int ipu6_isys_buffer_list_get(struct ipu6_isys_stream *stream, struct ipu6_isys_buffer_list *bl); void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream, struct ipu6_fw_isys_resp_info_abi *info); int ipu6_isys_queue_init(struct ipu6_isys_queue *aq); +void ipu6_stream_buf_ready(struct ipu6_isys_stream *stream, u8 pin_id, + u32 pin_addr, u64 time, bool error_check); #endif /* IPU6_ISYS_QUEUE_H */ diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c index dbd6f76a066d..60070842ee68 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c @@ -160,6 +160,7 @@ u32 ipu6_isys_convert_bayer_order(u32 code, int x, int y) } int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd, + const struct v4l2_subdev_client_info *ci, struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { @@ -265,17 +266,13 @@ static int subdev_set_routing(struct v4l2_subdev *sd, return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); } -u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad) +u32 __ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev_state *state, + u32 pad) { - struct v4l2_subdev_state *state; struct v4l2_subdev_route *routes; unsigned int i; u32 source_stream = 0; - state = v4l2_subdev_lock_and_get_active_state(sd); - if (!state) - return 0; - routes = state->routing.routes; for (i = 0; i < state->routing.num_routes; i++) { if (routes[i].source_pad == pad) { @@ -284,6 +281,20 @@ u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad) } } + return source_stream; +} + +u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad) +{ + struct v4l2_subdev_state *state; + u32 source_stream = 0; + + state = v4l2_subdev_lock_and_get_active_state(sd); + if (!state) + return 0; + + source_stream = __ipu6_isys_get_src_stream_by_src_pad(state, pad); + v4l2_subdev_unlock_state(state); return source_stream; diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h index 35069099c364..b892d96992af 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h @@ -31,12 +31,15 @@ bool ipu6_isys_is_bayer_format(u32 code); u32 ipu6_isys_convert_bayer_order(u32 code, int x, int y); int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd, + const struct v4l2_subdev_client_info *ci, struct v4l2_subdev_state *state, struct v4l2_subdev_format *fmt); int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code); +u32 __ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev_state *state, + u32 pad); u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad); int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c index 3ac48d2076da..129016e57446 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c @@ -432,185 +432,110 @@ unlock: return ret; } -static void get_stream_opened(struct ipu6_isys_video *av) -{ - unsigned long flags; - - spin_lock_irqsave(&av->isys->streams_lock, flags); - av->isys->stream_opened++; - spin_unlock_irqrestore(&av->isys->streams_lock, flags); -} +int ipu6_isys_fw_pins_prepare(struct ipu6_isys_stream *stream, + struct v4l2_mbus_frame_desc *desc, + int (*fw_pin_cfg)(struct ipu6_isys_video *av, + struct ipu6_isys_stream *stream, + struct media_pad *src_pad, + struct v4l2_mbus_frame_desc_entry *entry, + void *__cfg), void *stream_cfg) +{ + struct v4l2_subdev_state *csi2_state = + v4l2_subdev_get_locked_active_state(&stream->asd->sd); + struct device *dev = &stream->isys->adev->auxdev.dev; + struct ipu6_isys_queue *aq; -static void put_stream_opened(struct ipu6_isys_video *av) -{ - unsigned long flags; + list_for_each_entry(aq, &stream->queues, node) { + struct ipu6_isys_video *__av = ipu6_isys_queue_to_video(aq); + struct media_pad *remote_pad = + media_pad_remote_pad_first(&__av->pad); + u64 source_streams = 1; + unsigned int sink_stream = + __ffs(v4l2_subdev_state_xlate_streams(csi2_state, + remote_pad->index, + CSI2_PAD_SINK, + &source_streams)); + struct v4l2_mbus_frame_desc_entry *entry = NULL; + int ret; + + for (unsigned int i = 0; i < desc->num_entries; i++) { + if (desc->entry[i].stream == sink_stream) { + entry = &desc->entry[i]; + break; + } + } - spin_lock_irqsave(&av->isys->streams_lock, flags); - av->isys->stream_opened--; - spin_unlock_irqrestore(&av->isys->streams_lock, flags); -} + if (!entry) { + dev_err(dev, "cannot find frame desc entry for sink stream %u\n", + sink_stream); + return -EINVAL; + } -static int ipu6_isys_fw_pin_cfg(struct ipu6_isys_video *av, - struct ipu6_fw_isys_stream_cfg_data_abi *cfg) -{ - struct media_pad *src_pad = media_pad_remote_pad_first(&av->pad); - struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity); - struct v4l2_subdev_state *state = v4l2_subdev_get_locked_active_state(sd); - struct ipu6_fw_isys_input_pin_info_abi *input_pin; - struct ipu6_fw_isys_output_pin_info_abi *output_pin; - struct ipu6_isys_stream *stream = av->stream; - struct ipu6_isys_queue *aq = &av->aq; - struct v4l2_mbus_framefmt fmt; - const struct ipu6_isys_pixelformat *pfmt = - ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0); - struct v4l2_rect v4l2_crop; - struct ipu6_isys *isys = av->isys; - int input_pins = cfg->nof_input_pins++; - int output_pins; - u32 src_stream; - - src_stream = ipu6_isys_get_src_stream_by_src_pad(sd, src_pad->index); - fmt = *v4l2_subdev_state_get_format(state, src_pad->index, src_stream); - v4l2_crop = *v4l2_subdev_state_get_crop(state, src_pad->index, src_stream); - - input_pin = &cfg->input_pins[input_pins]; - input_pin->input_res.width = fmt.width; - input_pin->input_res.height = fmt.height; - input_pin->dt = av->dt; - input_pin->bits_per_pix = pfmt->bpp_packed; - input_pin->mapped_dt = 0x40; /* invalid mipi data type */ - input_pin->mipi_decompression = 0; - input_pin->capture_mode = IPU6_FW_ISYS_CAPTURE_MODE_REGULAR; - input_pin->mipi_store_mode = pfmt->bpp == pfmt->bpp_packed ? - IPU6_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER : - IPU6_FW_ISYS_MIPI_STORE_MODE_NORMAL; - input_pin->crop_first_and_last_lines = v4l2_crop.top & 1; - - output_pins = cfg->nof_output_pins++; - aq->fw_output = output_pins; - stream->output_pins_queue[output_pins] = aq; - - output_pin = &cfg->output_pins[output_pins]; - output_pin->input_pin_id = input_pins; - output_pin->output_res.width = ipu6_isys_get_frame_width(av); - output_pin->output_res.height = ipu6_isys_get_frame_height(av); - - output_pin->stride = ipu6_isys_get_bytes_per_line(av); - if (pfmt->bpp != pfmt->bpp_packed) - output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_RAW_SOC; - else - output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_MIPI; - output_pin->ft = pfmt->css_pixelformat; - output_pin->send_irq = 1; - memset(output_pin->ts_offsets, 0, sizeof(output_pin->ts_offsets)); - output_pin->s2m_pixel_soc_pixel_remapping = - S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING; - output_pin->csi_be_soc_pixel_remapping = - CSI_BE_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING; - - output_pin->snoopable = true; - output_pin->error_handling_enable = false; - output_pin->sensor_type = isys->sensor_type++; - if (isys->sensor_type > isys->pdata->ipdata->sensor_type_end) - isys->sensor_type = isys->pdata->ipdata->sensor_type_start; + ret = fw_pin_cfg(__av, stream, remote_pad, entry, stream_cfg); + if (ret < 0) + return ret; + } return 0; } -static int start_stream_firmware(struct ipu6_isys_video *av, - struct ipu6_isys_buffer_list *bl) +int ipu6_isys_start_stream_firmware(struct ipu6_isys_stream *stream, + struct ipu6_isys_buffer_list *bl, + struct v4l2_mbus_frame_desc *desc) { - struct ipu6_fw_isys_stream_cfg_data_abi *stream_cfg; - struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL; - struct ipu6_isys_stream *stream = av->stream; - struct device *dev = &av->isys->adev->auxdev.dev; + struct ipu6_bus_device *adev = stream->asd->isys->adev; + const struct ipu6_fw_isys_ops *fw_ops = adev->auxdrv_data->fw_ops; + struct device *dev = &adev->auxdev.dev; struct isys_fw_msgs *msg = NULL; - struct ipu6_isys_queue *aq; int ret, retout, tout; - u16 send_type; + bool capture = bl ? true : false; msg = ipu6_get_fw_msg_buf(stream); if (!msg) return -ENOMEM; - stream_cfg = &msg->fw_msg.stream; - stream_cfg->src = stream->stream_source; - stream_cfg->vc = stream->vc; - stream_cfg->isl_use = 0; - stream_cfg->sensor_type = IPU6_FW_ISYS_SENSOR_MODE_NORMAL; - - list_for_each_entry(aq, &stream->queues, node) { - struct ipu6_isys_video *__av = ipu6_isys_queue_to_video(aq); - - ret = ipu6_isys_fw_pin_cfg(__av, stream_cfg); - if (ret < 0) { - ipu6_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); - return ret; - } + ret = fw_ops->prepare_stream_cfg(stream, desc, msg); + if (ret < 0) { + ipu6_put_fw_msg_buf(stream->isys, msg); + return ret; } - ipu6_fw_isys_dump_stream_cfg(dev, stream_cfg); - - stream->nr_output_pins = stream_cfg->nof_output_pins; - reinit_completion(&stream->stream_open_completion); - ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle, - stream_cfg, msg->dma_addr, - sizeof(*stream_cfg), - IPU6_FW_ISYS_SEND_TYPE_STREAM_OPEN); + ret = fw_ops->stream_open(stream->isys, stream->stream_handle, msg); if (ret < 0) { dev_err(dev, "can't open stream (%d)\n", ret); - ipu6_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); + ipu6_put_fw_msg_buf(stream->isys, msg); return ret; } - get_stream_opened(av); - tout = wait_for_completion_timeout(&stream->stream_open_completion, IPU6_FW_CALL_TIMEOUT_JIFFIES); - ipu6_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); + ipu6_put_fw_msg_buf(stream->isys, msg); if (!tout) { dev_err(dev, "stream open time out\n"); - ret = -ETIMEDOUT; - goto out_put_stream_opened; + return -ETIMEDOUT; } if (stream->error) { dev_err(dev, "stream open error: %d\n", stream->error); - ret = -EIO; - goto out_put_stream_opened; + return -EIO; } dev_dbg(dev, "start stream: open complete\n"); - if (bl) { - msg = ipu6_get_fw_msg_buf(stream); - if (!msg) { - ret = -ENOMEM; - goto out_put_stream_opened; - } - buf = &msg->fw_msg.frame; - ipu6_isys_buf_to_fw_frame_buf(buf, stream, bl); - ipu6_isys_buffer_list_queue(bl, - IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0); + msg = ipu6_get_fw_msg_buf(stream); + if (!msg) { + return -ENOMEM; } - reinit_completion(&stream->stream_start_completion); + fw_ops->prepare_buf_set(msg, stream, bl); + ipu6_isys_buffer_list_queue(bl, IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0); - if (bl) { - send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE; - ipu6_fw_isys_dump_frame_buff_set(dev, buf, - stream_cfg->nof_output_pins); - ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle, - buf, msg->dma_addr, - sizeof(*buf), send_type); - } else { - send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START; - ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle, - send_type); - } + reinit_completion(&stream->stream_start_completion); + ret = fw_ops->stream_start(stream->isys, stream->stream_handle, msg, + capture); if (ret < 0) { dev_err(dev, "can't start streaming (%d)\n", ret); goto out_stream_close; @@ -635,12 +560,10 @@ static int start_stream_firmware(struct ipu6_isys_video *av, out_stream_close: reinit_completion(&stream->stream_close_completion); - retout = ipu6_fw_isys_simple_cmd(av->isys, - stream->stream_handle, - IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE); + retout = fw_ops->stream_close(stream->isys, stream->stream_handle); if (retout < 0) { dev_dbg(dev, "can't close stream (%d)\n", retout); - goto out_put_stream_opened; + return retout; } tout = wait_for_completion_timeout(&stream->stream_close_completion, @@ -652,23 +575,19 @@ out_stream_close: else dev_dbg(dev, "stream close complete\n"); -out_put_stream_opened: - put_stream_opened(av); - return ret; } -static void stop_streaming_firmware(struct ipu6_isys_video *av) +void ipu6_isys_stop_stream_firmware(struct ipu6_isys_stream *stream) { - struct device *dev = &av->isys->adev->auxdev.dev; - struct ipu6_isys_stream *stream = av->stream; + struct ipu6_bus_device *adev = stream->asd->isys->adev; + const struct ipu6_fw_isys_ops *fw_ops = adev->auxdrv_data->fw_ops; + struct device *dev = &adev->auxdev.dev; int ret, tout; reinit_completion(&stream->stream_stop_completion); - ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle, - IPU6_FW_ISYS_SEND_TYPE_STREAM_FLUSH); - + ret = fw_ops->stream_flush(stream->isys, stream->stream_handle); if (ret < 0) { dev_err(dev, "can't stop stream (%d)\n", ret); return; @@ -684,16 +603,17 @@ static void stop_streaming_firmware(struct ipu6_isys_video *av) dev_dbg(dev, "stop stream: complete\n"); } -static void close_streaming_firmware(struct ipu6_isys_video *av) +void ipu6_isys_close_stream_firmware(struct ipu6_isys_stream *stream) { - struct ipu6_isys_stream *stream = av->stream; - struct device *dev = &av->isys->adev->auxdev.dev; + struct ipu6_bus_device *adev = stream->asd->isys->adev; + const struct ipu6_fw_isys_ops *fw_ops = adev->auxdrv_data->fw_ops; + struct device *dev = &adev->auxdev.dev; + struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd); int ret, tout; reinit_completion(&stream->stream_close_completion); - ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle, - IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE); + ret = fw_ops->stream_close(stream->isys, stream->stream_handle); if (ret < 0) { dev_err(dev, "can't close stream (%d)\n", ret); return; @@ -708,349 +628,153 @@ static void close_streaming_firmware(struct ipu6_isys_video *av) else dev_dbg(dev, "close stream: complete\n"); - put_stream_opened(av); + scoped_guard(spinlock_irqsave, &stream->isys->streams_lock) { + stream->isys->streams_by_handle[stream->stream_handle] = NULL; + csi2->streams_by_vc[stream->vc] = NULL; + } } -int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av, - struct media_entity *source_entity, - int nr_queues) +struct ipu6_isys_stream * +ipu6_isys_find_stream_firmware(struct ipu6_isys_csi2 *csi2, u8 vc) { - struct ipu6_isys_stream *stream = av->stream; - struct ipu6_isys_csi2 *csi2; + struct ipu6_isys_stream *stream; - if (WARN_ON(stream->nr_streaming)) - return -EINVAL; + list_for_each_entry(stream, &csi2->streams, csi2_entry) + if (stream->vc == vc) + return stream; - stream->nr_queues = nr_queues; - atomic_set(&stream->sequence, 0); - - stream->seq_index = 0; - memset(stream->seq, 0, sizeof(stream->seq)); - - if (WARN_ON(!list_empty(&stream->queues))) - return -EINVAL; - - stream->stream_source = stream->asd->source; - csi2 = ipu6_isys_subdev_to_csi2(stream->asd); - csi2->receiver_errors = 0; - - dev_dbg(&av->isys->adev->auxdev.dev, - "prepare stream: external entity %s\n", - source_entity->name); - - return 0; + return NULL; } -void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av, - struct media_entity *source) +void ipu6_isys_free_stream_firmware(struct ipu6_isys_stream *stream) { - struct ipu6_isys *isys = av->isys; - struct ipu6_isys_csi2 *csi2 = NULL; - struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark; - struct device *dev = &isys->adev->auxdev.dev; - struct v4l2_mbus_framefmt format; - struct v4l2_subdev *esd; - struct v4l2_control hb = { .id = V4L2_CID_HBLANK, .value = 0 }; - unsigned int bpp, lanes; - s64 link_freq = 0; - u64 pixel_rate = 0; - int ret; - - esd = media_entity_to_v4l2_subdev(source); - - av->watermark.width = ipu6_isys_get_frame_width(av); - av->watermark.height = ipu6_isys_get_frame_height(av); - av->watermark.sram_gran_shift = isys->pdata->ipdata->sram_gran_shift; - av->watermark.sram_gran_size = isys->pdata->ipdata->sram_gran_size; - - ret = v4l2_g_ctrl(esd->ctrl_handler, &hb); - if (!ret && hb.value >= 0) - av->watermark.hblank = hb.value; - else - av->watermark.hblank = 0; - - csi2 = ipu6_isys_subdev_to_csi2(av->stream->asd); - link_freq = ipu6_isys_csi2_get_link_freq(csi2); - if (link_freq > 0) { - struct v4l2_subdev_state *state = - v4l2_subdev_lock_and_get_active_state(&csi2->asd.sd); - - lanes = csi2->nlanes; - format = *v4l2_subdev_state_get_format(state, 0, - av->source_stream); - bpp = ipu6_isys_mbus_code_to_bpp(format.code); - pixel_rate = mul_u64_u32_div(link_freq, lanes * 2, bpp); + struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd); + struct ipu6_isys_queue *aq, *aq_safe; - v4l2_subdev_unlock_state(state); - } - - av->watermark.pixel_rate = pixel_rate; + list_for_each_entry_safe(aq, aq_safe, &stream->queues, node) { + struct ipu6_isys_video *av = + container_of_const(aq, struct ipu6_isys_video, aq); - if (!pixel_rate) { - mutex_lock(&iwake_watermark->mutex); - iwake_watermark->force_iwake_disable = true; - mutex_unlock(&iwake_watermark->mutex); - dev_warn(dev, "unexpected pixel_rate from %s, disable iwake.\n", - source->name); + list_del(&aq->node); + av->stream = NULL; } -} -static void calculate_stream_datarate(struct ipu6_isys_video *av) -{ - struct video_stream_watermark *watermark = &av->watermark; - const struct ipu6_isys_pixelformat *pfmt = - ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0); - u32 pages_per_line, pb_bytes_per_line, pixels_per_line, bytes_per_line; - u64 line_time_ns, stream_data_rate; - u16 shift, size; - - shift = watermark->sram_gran_shift; - size = watermark->sram_gran_size; - - pixels_per_line = watermark->width + watermark->hblank; - line_time_ns = div_u64(pixels_per_line * NSEC_PER_SEC, - watermark->pixel_rate); - bytes_per_line = watermark->width * pfmt->bpp / 8; - pages_per_line = DIV_ROUND_UP(bytes_per_line, size); - pb_bytes_per_line = pages_per_line << shift; - stream_data_rate = div64_u64(pb_bytes_per_line * 1000, line_time_ns); - - watermark->stream_data_rate = stream_data_rate; + list_del(&stream->csi2_entry); + ida_free(&csi2->isys->streams, stream->stream_handle); + kfree(stream); } -void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state) -{ - struct isys_iwake_watermark *iwake_watermark = - &av->isys->iwake_watermark; - - if (!av->watermark.pixel_rate) - return; +struct ipu6_isys_stream * +ipu6_isys_alloc_stream_firmware(struct ipu6_isys_csi2 *csi2, + struct v4l2_subdev_state *csi2_state, + struct v4l2_mbus_frame_desc *desc, + u8 vc) +{ + struct device *dev = &csi2->isys->adev->auxdev.dev; + struct ipu6_isys_stream *stream; + struct v4l2_subdev_route *route; + int ret; - if (state) { - calculate_stream_datarate(av); - mutex_lock(&iwake_watermark->mutex); - list_add(&av->watermark.stream_node, - &iwake_watermark->video_list); - mutex_unlock(&iwake_watermark->mutex); - } else { - av->watermark.stream_data_rate = 0; - mutex_lock(&iwake_watermark->mutex); - list_del(&av->watermark.stream_node); - mutex_unlock(&iwake_watermark->mutex); - } + stream = kzalloc_obj(*stream); + if (!stream) + return ERR_PTR(-ENOMEM); - update_watermark_setting(av->isys); -} + ret = ida_alloc_max(&csi2->isys->streams, IPU6_ISYS_MAX_STREAMS - 1, + GFP_KERNEL); + if (ret < 0) + goto err_free_stream; -void ipu6_isys_put_stream(struct ipu6_isys_stream *stream) -{ - struct device *dev; - unsigned int i; - unsigned long flags; + stream->stream_handle = ret; + mutex_init(&stream->mutex); + init_completion(&stream->stream_open_completion); + init_completion(&stream->stream_close_completion); + init_completion(&stream->stream_start_completion); + init_completion(&stream->stream_stop_completion); + INIT_LIST_HEAD(&stream->queues); + stream->isys = csi2->asd.isys; + stream->asd = &csi2->asd; + stream->vc = vc; - if (!stream) { - pr_err("ipu6-isys: no available stream\n"); - return; + scoped_guard(spinlock_irqsave, &stream->isys->streams_lock) { + stream->isys->streams_by_handle[stream->stream_handle] = stream; + csi2->streams_by_vc[stream->vc] = stream; } - dev = &stream->isys->adev->auxdev.dev; - - spin_lock_irqsave(&stream->isys->streams_lock, flags); - for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) { - if (&stream->isys->streams[i] == stream) { - if (stream->isys->streams_ref_count[i] > 0) - stream->isys->streams_ref_count[i]--; - else - dev_warn(dev, "invalid stream %d\n", i); - - break; - } - } - spin_unlock_irqrestore(&stream->isys->streams_lock, flags); -} + list_add(&stream->csi2_entry, &csi2->streams); -static struct ipu6_isys_stream * -ipu6_isys_get_stream(struct ipu6_isys_video *av, struct ipu6_isys_subdev *asd) -{ - struct ipu6_isys_stream *stream = NULL; - struct ipu6_isys *isys = av->isys; - unsigned long flags; - unsigned int i; - u8 vc = av->vc; + for_each_active_route(&csi2_state->routing, route) { + struct media_pad *vdev_pad = + media_pad_remote_pad_first(&csi2->asd.pad[route->source_pad]); + struct v4l2_mbus_frame_desc_entry *entry = NULL; - if (!isys) - return NULL; + for (unsigned int i = 0; i < desc->num_entries; i++) { + if (desc->entry[i].stream != route->sink_stream) + continue; - spin_lock_irqsave(&isys->streams_lock, flags); - for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) { - if (isys->streams_ref_count[i] && isys->streams[i].vc == vc && - isys->streams[i].asd == asd) { - isys->streams_ref_count[i]++; - stream = &isys->streams[i]; + entry = &desc->entry[i]; break; } - } - if (!stream) { - for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) { - if (!isys->streams_ref_count[i]) { - isys->streams_ref_count[i]++; - stream = &isys->streams[i]; - stream->vc = vc; - stream->asd = asd; - break; - } + if (!entry) { + dev_dbg(dev, "cannot find stream %u in frame desc\n", + route->sink_stream); + ret = -EINVAL; + goto err_ida_free; } - } - spin_unlock_irqrestore(&isys->streams_lock, flags); - - return stream; -} -struct ipu6_isys_stream * -ipu6_isys_query_stream_by_handle(struct ipu6_isys *isys, u8 stream_handle) -{ - unsigned long flags; - struct ipu6_isys_stream *stream = NULL; - - if (!isys) - return NULL; - - if (stream_handle >= IPU6_ISYS_MAX_STREAMS) { - dev_err(&isys->adev->auxdev.dev, - "stream_handle %d is invalid\n", stream_handle); - return NULL; - } - - spin_lock_irqsave(&isys->streams_lock, flags); - if (isys->streams_ref_count[stream_handle] > 0) { - isys->streams_ref_count[stream_handle]++; - stream = &isys->streams[stream_handle]; - } - spin_unlock_irqrestore(&isys->streams_lock, flags); - - return stream; -} - -struct ipu6_isys_stream * -ipu6_isys_query_stream_by_source(struct ipu6_isys *isys, int source, u8 vc) -{ - struct ipu6_isys_stream *stream = NULL; - unsigned long flags; - unsigned int i; - - if (!isys) - return NULL; + if (entry->bus.csi2.vc != vc) + continue; - if (source < 0) { - dev_err(&isys->adev->auxdev.dev, - "query stream with invalid port number\n"); - return NULL; - } + struct ipu6_isys_video *av = + container_of_const(vdev_pad, struct ipu6_isys_video, + pad); - spin_lock_irqsave(&isys->streams_lock, flags); - for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) { - if (!isys->streams_ref_count[i]) - continue; + list_add(&av->aq.node, &stream->queues); - if (isys->streams[i].stream_source == source && - isys->streams[i].vc == vc) { - stream = &isys->streams[i]; - isys->streams_ref_count[i]++; - break; - } + stream->nr_output_pins++; + av->stream = stream; } - spin_unlock_irqrestore(&isys->streams_lock, flags); return stream; -} - -static u64 get_stream_mask_by_pipeline(struct ipu6_isys_video *__av) -{ - struct media_pipeline *pipeline = - media_entity_pipeline(&__av->vdev.entity); - unsigned int i; - u64 stream_mask = 0; - for (i = 0; i < NR_OF_CSI2_SRC_PADS; i++) { - struct ipu6_isys_video *av = &__av->csi2->av[i]; +err_ida_free: + list_del(&stream->csi2_entry); + ida_free(&csi2->isys->streams, stream->stream_handle); - if (pipeline == media_entity_pipeline(&av->vdev.entity)) - stream_mask |= BIT_ULL(av->source_stream); - } +err_free_stream: + kfree(stream); - return stream_mask; + return ERR_PTR(ret); } -int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, - struct ipu6_isys_buffer_list *bl) +int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state) { - struct v4l2_subdev_krouting *routing; - struct ipu6_isys_stream *stream = av->stream; - struct v4l2_subdev_state *subdev_state; struct device *dev = &av->isys->adev->auxdev.dev; struct v4l2_subdev *sd; struct media_pad *r_pad; - u32 sink_pad, sink_stream; - u64 r_stream; - u64 stream_mask = 0; int ret = 0; - dev_dbg(dev, "set stream: %d\n", state); - - sd = &stream->asd->sd; + sd = &av->csi2->asd.sd; r_pad = media_pad_remote_pad_first(&av->pad); - r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index); - - subdev_state = v4l2_subdev_lock_and_get_active_state(sd); - routing = &subdev_state->routing; - ret = v4l2_subdev_routing_find_opposite_end(routing, r_pad->index, - r_stream, &sink_pad, - &sink_stream); - v4l2_subdev_unlock_state(subdev_state); - if (ret) - return ret; - stream_mask = get_stream_mask_by_pipeline(av); if (!state) { - stop_streaming_firmware(av); - /* stop sub-device which connects with video */ - dev_dbg(dev, "stream off entity %s pad:%d mask:0x%llx\n", - sd->name, r_pad->index, stream_mask); - ret = v4l2_subdev_disable_streams(sd, r_pad->index, - stream_mask); + dev_dbg(dev, "stream off %s pad:%d\n", sd->name, r_pad->index); + ret = v4l2_subdev_disable_streams(sd, r_pad->index, 1); if (ret) dev_err(dev, "stream off %s failed with %d\n", sd->name, ret); - - close_streaming_firmware(av); } else { - ret = start_stream_firmware(av, bl); - if (ret) { - dev_err(dev, "start stream of firmware failed\n"); - return ret; - } - /* start sub-device which connects with video */ - dev_dbg(dev, "stream on %s pad %d mask 0x%llx\n", sd->name, - r_pad->index, stream_mask); - ret = v4l2_subdev_enable_streams(sd, r_pad->index, stream_mask); - if (ret) { + dev_dbg(dev, "stream on %s pad %d\n", sd->name, r_pad->index); + ret = v4l2_subdev_enable_streams(sd, r_pad->index, 1); + if (ret) dev_err(dev, "stream on %s failed with %d\n", sd->name, ret); - goto out_media_entity_stop_streaming_firmware; - } } av->streaming = state; - return 0; - -out_media_entity_stop_streaming_firmware: - stop_streaming_firmware(av); - close_streaming_firmware(av); - return ret; } @@ -1089,148 +813,6 @@ static const struct v4l2_file_operations isys_fops = { .release = vb2_fop_release, }; -int ipu6_isys_fw_open(struct ipu6_isys *isys) -{ - struct ipu6_bus_device *adev = isys->adev; - const struct ipu6_isys_internal_pdata *ipdata = isys->pdata->ipdata; - int ret; - - ret = pm_runtime_resume_and_get(&adev->auxdev.dev); - if (ret < 0) - return ret; - - mutex_lock(&isys->mutex); - - if (isys->ref_count++) - goto unlock; - - ipu6_configure_spc(adev->isp, &ipdata->hw_variant, - IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX, isys->pdata->base, - adev->pkg_dir, adev->pkg_dir_dma_addr); - - /* - * Buffers could have been left to wrong queue at last closure. - * Move them now back to empty buffer queue. - */ - ipu6_cleanup_fw_msg_bufs(isys); - - if (isys->fwcom) { - /* - * Something went wrong in previous shutdown. As we are now - * restarting isys we can safely delete old context. - */ - dev_warn(&adev->auxdev.dev, "clearing old context\n"); - ipu6_fw_isys_cleanup(isys); - } - - ret = ipu6_fw_isys_init(isys, ipdata->num_parallel_streams); - if (ret < 0) - goto out; - -unlock: - mutex_unlock(&isys->mutex); - - return 0; - -out: - isys->ref_count--; - mutex_unlock(&isys->mutex); - pm_runtime_put(&adev->auxdev.dev); - - return ret; -} - -void ipu6_isys_fw_close(struct ipu6_isys *isys) -{ - mutex_lock(&isys->mutex); - - isys->ref_count--; - if (!isys->ref_count) { - ipu6_fw_isys_close(isys); - if (isys->fwcom) { - isys->need_reset = true; - dev_warn(&isys->adev->auxdev.dev, - "failed to close fw isys\n"); - } - } - - mutex_unlock(&isys->mutex); - - if (isys->need_reset) - pm_runtime_put_sync(&isys->adev->auxdev.dev); - else - pm_runtime_put(&isys->adev->auxdev.dev); -} - -int ipu6_isys_setup_video(struct ipu6_isys_video *av, - struct media_pad *remote_pad, - struct media_pad *source_pad, int *nr_queues) -{ - const struct ipu6_isys_pixelformat *pfmt = - ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0); - struct device *dev = &av->isys->adev->auxdev.dev; - struct v4l2_mbus_frame_desc_entry entry; - struct v4l2_subdev_route *route = NULL; - struct v4l2_subdev_route *r; - struct v4l2_subdev_state *state; - struct v4l2_subdev *remote_sd = - media_entity_to_v4l2_subdev(remote_pad->entity); - struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(remote_sd); - int ret = -EINVAL; - - *nr_queues = 0; - - /* Find the root */ - state = v4l2_subdev_lock_and_get_active_state(remote_sd); - for_each_active_route(&state->routing, r) { - (*nr_queues)++; - - if (r->source_pad == remote_pad->index) - route = r; - } - - if (!route) { - v4l2_subdev_unlock_state(state); - dev_dbg(dev, "Failed to find route\n"); - return -ENODEV; - } - av->source_stream = route->sink_stream; - v4l2_subdev_unlock_state(state); - - ret = ipu6_isys_csi2_get_remote_desc(av->source_stream, - to_ipu6_isys_csi2(asd), - source_pad->entity, &entry); - if (ret == -ENOIOCTLCMD) { - av->vc = 0; - av->dt = ipu6_isys_mbus_code_to_mipi(pfmt->code); - } else if (!ret) { - dev_dbg(dev, "Framedesc: stream %u, len %u, vc %u, dt %#x\n", - entry.stream, entry.length, entry.bus.csi2.vc, - entry.bus.csi2.dt); - - av->vc = entry.bus.csi2.vc; - av->dt = entry.bus.csi2.dt; - } else { - dev_err(dev, "failed to get remote frame desc\n"); - return ret; - } - - ret = video_device_pipeline_alloc_start(&av->vdev); - if (ret < 0) { - dev_dbg(dev, "media pipeline start failed\n"); - return ret; - } - - av->stream = ipu6_isys_get_stream(av, asd); - if (!av->stream) { - video_device_pipeline_stop(&av->vdev); - dev_err(dev, "no available stream for firmware\n"); - return -EINVAL; - } - - return 0; -} - /* * Do everything that's needed to initialise things related to video * buffer queue, video node, and the related media entity. The caller @@ -1262,7 +844,7 @@ int ipu6_isys_video_init(struct ipu6_isys_video *av) ret = ipu6_isys_queue_init(&av->aq); if (ret) - goto out_free_watermark; + goto out_mutex_destroy; av->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; ret = media_entity_pads_init(&av->vdev.entity, 1, &av->pad); @@ -1299,7 +881,7 @@ out_media_entity_cleanup: out_vb2_queue_release: vb2_queue_release(&av->aq.vbq); -out_free_watermark: +out_mutex_destroy: mutex_destroy(&av->mutex); return ret; diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h index 2ff53315d7b9..2821b9b1b943 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h @@ -22,6 +22,7 @@ struct file; struct ipu6_isys; struct ipu6_isys_csi2; struct ipu6_isys_subdev; +struct v4l2_mbus_frame_desc; struct ipu6_isys_pixelformat { u32 pixelformat; @@ -44,17 +45,15 @@ struct sequence_info { struct ipu6_isys_stream { struct mutex mutex; atomic_t sequence; + atomic_t buf_id; unsigned int seq_index; struct sequence_info seq[IPU6_ISYS_MAX_PARALLEL_SOF]; - int stream_source; int stream_handle; unsigned int nr_output_pins; struct ipu6_isys_subdev *asd; - - int nr_queues; /* Number of capture queues */ - int nr_streaming; - int streaming; /* Has streaming been really started? */ struct list_head queues; + struct list_head csi2_entry; + struct completion stream_open_completion; struct completion stream_close_completion; struct completion stream_start_completion; @@ -66,20 +65,9 @@ struct ipu6_isys_stream { u8 vc; }; -struct video_stream_watermark { - u32 width; - u32 height; - u32 hblank; - u32 frame_rate; - u64 pixel_rate; - u64 stream_data_rate; - u16 sram_gran_shift; - u16 sram_gran_size; - struct list_head stream_node; -}; - struct ipu6_isys_video { struct ipu6_isys_queue aq; + struct list_head csi2_entry; /* Serialise access to other fields in the struct. */ struct mutex mutex; struct media_pad pad; @@ -90,10 +78,7 @@ struct ipu6_isys_video { struct ipu6_isys_csi2 *csi2; struct ipu6_isys_stream *stream; unsigned int streaming; - struct video_stream_watermark watermark; u32 source_stream; - u8 vc; - u8 dt; }; #define ipu6_isys_queue_to_video(__aq) \ @@ -104,27 +89,34 @@ extern const struct ipu6_isys_pixelformat ipu6_isys_pfmts_packed[]; const struct ipu6_isys_pixelformat * ipu6_isys_get_isys_format(u32 pixelformat, u32 code); -int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av, - struct media_entity *source_entity, - int nr_queues); -int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, - struct ipu6_isys_buffer_list *bl); +int ipu6_isys_fw_pins_prepare(struct ipu6_isys_stream *stream, + struct v4l2_mbus_frame_desc *desc, + int (*fw_pin_cfg)(struct ipu6_isys_video *av, + struct ipu6_isys_stream *stream, + struct media_pad *src_pad, + struct v4l2_mbus_frame_desc_entry *entry, + void *__cfg), void *stream_cfg); +int ipu6_isys_start_stream_firmware(struct ipu6_isys_stream *stream, + struct ipu6_isys_buffer_list *bl, + struct v4l2_mbus_frame_desc *desc); +void ipu6_isys_stop_stream_firmware(struct ipu6_isys_stream *stream); +void ipu6_isys_close_stream_firmware(struct ipu6_isys_stream *stream); +struct ipu6_isys_stream * +ipu6_isys_find_stream_firmware(struct ipu6_isys_csi2 *csi2, u8 vc); +void ipu6_isys_free_stream_firmware(struct ipu6_isys_stream *stream); +struct ipu6_isys_stream * +ipu6_isys_alloc_stream_firmware(struct ipu6_isys_csi2 *csi2, + struct v4l2_subdev_state *state, + struct v4l2_mbus_frame_desc *desc, + u8 vc); +int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state); int ipu6_isys_fw_open(struct ipu6_isys *isys); void ipu6_isys_fw_close(struct ipu6_isys *isys); int ipu6_isys_setup_video(struct ipu6_isys_video *av, struct media_pad *remote_pad, - struct media_pad *source_pad, int *nr_queues); + struct media_pad *source_pad); int ipu6_isys_video_init(struct ipu6_isys_video *av); void ipu6_isys_video_cleanup(struct ipu6_isys_video *av); -void ipu6_isys_put_stream(struct ipu6_isys_stream *stream); -struct ipu6_isys_stream * -ipu6_isys_query_stream_by_handle(struct ipu6_isys *isys, u8 stream_handle); -struct ipu6_isys_stream * -ipu6_isys_query_stream_by_source(struct ipu6_isys *isys, int source, u8 vc); - -void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av, - struct media_entity *source); -void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state); u32 ipu6_isys_get_format(struct ipu6_isys_video *av); u32 ipu6_isys_get_data_size(struct ipu6_isys_video *av); diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.c b/drivers/media/pci/intel/ipu6/ipu6-isys.c index 24db2763de54..60f5f9ea2910 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys.c @@ -41,6 +41,9 @@ #include "ipu6-platform-buttress-regs.h" #include "ipu6-platform-isys-csi2-reg.h" #include "ipu6-platform-regs.h" +#include "ipu7-isys-csi-phy.h" +#include "ipu7-isys-csi2-regs.h" +#include "ipu7-platform-regs.h" #define IPU6_BUTTRESS_FABIC_CONTROL 0x68 #define GDA_ENABLE_IWAKE_INDEX 2 @@ -98,9 +101,28 @@ enum ltr_did_type { LTR_TYPE_MAX }; -#define ISYS_PM_QOS_VALUE 300 +struct ltr_did { + union { + u32 value; + struct { + u8 val0; + u8 val1; + u8 val2; + u8 val3; + } bits; + } lut_ltr; + union { + u32 value; + struct { + u8 th0; + u8 th1; + u8 th2; + u8 th3; + } bits; + } lut_fill_time; +}; -static int isys_isr_one(struct ipu6_bus_device *adev); +#define ISYS_PM_QOS_VALUE 300 static int isys_complete_ext_device_registration(struct ipu6_isys *isys, @@ -132,6 +154,9 @@ isys_complete_ext_device_registration(struct ipu6_isys *isys, } isys->csi2[csi2->port].nlanes = csi2->nlanes; + isys->csi2[csi2->port].phy_mode = + csi2->bus_type == V4L2_MBUS_CSI2_DPHY ? + PHY_MODE_DPHY : PHY_MODE_CPHY; return 0; @@ -141,23 +166,6 @@ unregister_subdev: return ret; } -static void isys_stream_init(struct ipu6_isys *isys) -{ - u32 i; - - for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) { - mutex_init(&isys->streams[i].mutex); - init_completion(&isys->streams[i].stream_open_completion); - init_completion(&isys->streams[i].stream_close_completion); - init_completion(&isys->streams[i].stream_start_completion); - init_completion(&isys->streams[i].stream_stop_completion); - INIT_LIST_HEAD(&isys->streams[i].queues); - isys->streams[i].isys = isys; - isys->streams[i].stream_handle = i; - isys->streams[i].vc = INVALID_VC_ID; - } -} - static void isys_csi2_unregister_subdevices(struct ipu6_isys *isys) { const struct ipu6_isys_internal_csi2_pdata *csi2 = @@ -176,13 +184,23 @@ static int isys_csi2_register_subdevices(struct ipu6_isys *isys) int ret; for (i = 0; i < csi2_pdata->nports; i++) { - ret = ipu6_isys_csi2_init(&isys->csi2[i], isys, - isys->pdata->base + - CSI_REG_PORT_BASE(i), i); + void __iomem *base = isys->pdata->base; + + if (IS_IPU7(isys->adev->isp)) { + u32 mask = IS_IPU7_MTL(isys->adev->isp) ? + IPU7_CSI_LEGACY_IRQ_MASK(i) : + IPU7P5_CSI_LEGACY_IRQ_MASK(i); + + isys->isr_csi2_bits |= mask; + isys->csi2[i].legacy_irq_mask = mask; + } else { + base += CSI_REG_PORT_BASE(i); + isys->isr_csi2_bits |= IPU6_ISYS_UNISPART_IRQ_CSI2(i); + } + + ret = ipu6_isys_csi2_init(&isys->csi2[i], isys, base, i); if (ret) goto fail; - - isys->isr_csi2_bits |= IPU6_ISYS_UNISPART_IRQ_CSI2(i); } return 0; @@ -269,7 +287,36 @@ fail: return ret; } -static void isys_setup_hw(struct ipu6_isys *isys) +static void ipu7_isys_setup_hw(struct ipu6_isys *isys) +{ + u32 offset, mask; + void __iomem *base = isys->pdata->base; + + offset = IPU7_IS_IO_GPREGS_BASE; + + writel(0x0, base + offset + IPU7_CLK_EN_TXCLKESC); + /* Update if ISYS freq updated (0: 400/1, 1:400/2, 63:400/64) */ + writel(0x0, base + offset + IPU7_CLK_DIV_FACTOR_IS_CLK); + /* correct the initial printf configuration */ + writel(0x200, base + IPU7_IS_UC_CTRL_BASE + IPU7_REG_PRINTF_AXI_CNTL); + + offset = IPU7_IS_UC_CTRL_BASE; + mask = IPU7_IS_UC_TO_SW_IRQ_MASK; + + writel(mask, base + offset + IPU7_TO_SW_IRQ_CNTL_CLEAR); + writel(mask, base + offset + IPU7_TO_SW_IRQ_CNTL_MASK_N); + writel(mask, base + offset + IPU7_TO_SW_IRQ_CNTL_ENABLE); + + offset = IPU7_IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE; + mask = IPU7_CSI_RX_LEGACY_IRQ_MASK; + + writel(mask, base + offset + IPU7_IRQ_CTL_EDGE); + writel(mask, base + offset + IPU7_IRQ_CTL_CLEAR); + writel(mask, base + offset + IPU7_IRQ_CTL_MASK); + writel(mask, base + offset + IPU7_IRQ_CTL_ENABLE); +} + +static void ipu6_isys_setup_hw(struct ipu6_isys *isys) { void __iomem *base = isys->pdata->base; const u8 *thd = isys->pdata->ipdata->hw_variant.cdc_fifo_threshold; @@ -304,118 +351,6 @@ static void isys_setup_hw(struct ipu6_isys *isys) writel(thd[i], base + IPU6_REG_ISYS_CDC_THRESHOLD(i)); } -static void ipu6_isys_csi2_isr(struct ipu6_isys_csi2 *csi2) -{ - struct ipu6_isys_stream *stream; - unsigned int i; - u32 status; - int source; - - ipu6_isys_register_errors(csi2); - - status = readl(csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + - CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET); - - writel(status, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + - CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET); - - source = csi2->asd.source; - for (i = 0; i < NR_OF_CSI2_VC; i++) { - if (status & IPU_CSI_RX_IRQ_FS_VC(i)) { - stream = ipu6_isys_query_stream_by_source(csi2->isys, - source, i); - if (stream) { - ipu6_isys_csi2_sof_event_by_stream(stream); - ipu6_isys_put_stream(stream); - } - } - - if (status & IPU_CSI_RX_IRQ_FE_VC(i)) { - stream = ipu6_isys_query_stream_by_source(csi2->isys, - source, i); - if (stream) { - ipu6_isys_csi2_eof_event_by_stream(stream); - ipu6_isys_put_stream(stream); - } - } - } -} - -static irqreturn_t isys_isr(struct ipu6_bus_device *adev) -{ - struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); - void __iomem *base = isys->pdata->base; - u32 status_sw, status_csi; - u32 ctrl0_status, ctrl0_clear; - - spin_lock(&isys->power_lock); - if (!isys->power) { - spin_unlock(&isys->power_lock); - return IRQ_NONE; - } - - ctrl0_status = isys->pdata->ipdata->csi2.ctrl0_irq_status; - ctrl0_clear = isys->pdata->ipdata->csi2.ctrl0_irq_clear; - - status_csi = readl(isys->pdata->base + ctrl0_status); - status_sw = readl(isys->pdata->base + - IPU6_REG_ISYS_UNISPART_IRQ_STATUS); - - writel(ISYS_UNISPART_IRQS & ~IPU6_ISYS_UNISPART_IRQ_SW, - base + IPU6_REG_ISYS_UNISPART_IRQ_MASK); - - do { - writel(status_csi, isys->pdata->base + ctrl0_clear); - - writel(status_sw, isys->pdata->base + - IPU6_REG_ISYS_UNISPART_IRQ_CLEAR); - - if (isys->isr_csi2_bits & status_csi) { - unsigned int i; - - for (i = 0; i < isys->pdata->ipdata->csi2.nports; i++) { - /* irq from not enabled port */ - if (!isys->csi2[i].base) - continue; - if (status_csi & IPU6_ISYS_UNISPART_IRQ_CSI2(i)) - ipu6_isys_csi2_isr(&isys->csi2[i]); - } - } - - writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_REG); - - if (!isys_isr_one(adev)) - status_sw = IPU6_ISYS_UNISPART_IRQ_SW; - else - status_sw = 0; - - status_csi = readl(isys->pdata->base + ctrl0_status); - status_sw |= readl(isys->pdata->base + - IPU6_REG_ISYS_UNISPART_IRQ_STATUS); - } while ((status_csi & isys->isr_csi2_bits) || - (status_sw & IPU6_ISYS_UNISPART_IRQ_SW)); - - writel(ISYS_UNISPART_IRQS, base + IPU6_REG_ISYS_UNISPART_IRQ_MASK); - - spin_unlock(&isys->power_lock); - - return IRQ_HANDLED; -} - -static void get_lut_ltrdid(struct ipu6_isys *isys, struct ltr_did *pltr_did) -{ - struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark; - struct ltr_did ltrdid_default; - - ltrdid_default.lut_ltr.value = LTR_DEFAULT_VALUE; - ltrdid_default.lut_fill_time.value = FILL_TIME_DEFAULT_VALUE; - - if (iwake_watermark->ltrdid.lut_ltr.value) - *pltr_did = iwake_watermark->ltrdid; - else - *pltr_did = ltrdid_default; -} - static int set_iwake_register(struct ipu6_isys *isys, u32 index, u32 value) { struct device *dev = &isys->adev->auxdev.dev; @@ -511,69 +446,57 @@ static void set_iwake_ltrdid(struct ipu6_isys *isys, u16 ltr, u16 did, */ static void enable_iwake(struct ipu6_isys *isys, bool enable) { - struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark; int ret; - mutex_lock(&iwake_watermark->mutex); - - if (iwake_watermark->iwake_enabled == enable) { - mutex_unlock(&iwake_watermark->mutex); + if (isys->iwake_watermark_enabled == enable) return; - } ret = set_iwake_register(isys, GDA_ENABLE_IWAKE_INDEX, enable); if (!ret) - iwake_watermark->iwake_enabled = enable; - - mutex_unlock(&iwake_watermark->mutex); + isys->iwake_watermark_enabled = enable; } -void update_watermark_setting(struct ipu6_isys *isys) +void ipu6_isys_update_watermark_setting(struct ipu6_isys *isys) { - struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark; u32 iwake_threshold, iwake_critical_threshold, page_num; struct device *dev = &isys->adev->auxdev.dev; u32 calc_fill_time_us = 0, ltr = 0, did = 0; - struct video_stream_watermark *p_watermark; enum ltr_did_type ltr_did_type; - struct list_head *stream_node; u64 isys_pb_datarate_mbs = 0; u32 mem_open_threshold = 0; struct ltr_did ltrdid; u64 threshold_bytes; u32 max_sram_size; u32 shift; + bool force_iwake_disable = false; + + lockdep_assert_held(&isys->stream_mutex); shift = isys->pdata->ipdata->sram_gran_shift; max_sram_size = isys->pdata->ipdata->max_sram_size; - mutex_lock(&iwake_watermark->mutex); - if (iwake_watermark->force_iwake_disable) { + for (unsigned int i = 0; i < isys->pdata->ipdata->csi2.nports; i++) { + isys_pb_datarate_mbs += + isys->csi2[i].watermark.stream_data_rate; + force_iwake_disable |= + isys->csi2[i].watermark.force_iwake_disable; + } + + if (force_iwake_disable) { + dev_dbg(dev, "watermark: forcing iwake disabled\n"); set_iwake_ltrdid(isys, 0, 0, LTR_IWAKE_OFF); set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX, CRITICAL_THRESHOLD_IWAKE_DISABLE); - goto unlock_exit; - } - - if (list_empty(&iwake_watermark->video_list)) { - isys_pb_datarate_mbs = 0; - } else { - list_for_each(stream_node, &iwake_watermark->video_list) { - p_watermark = list_entry(stream_node, - struct video_stream_watermark, - stream_node); - isys_pb_datarate_mbs += p_watermark->stream_data_rate; - } + return; } - mutex_unlock(&iwake_watermark->mutex); if (!isys_pb_datarate_mbs) { + dev_dbg(dev, "watermark: disabled iwake\n"); enable_iwake(isys, false); set_iwake_ltrdid(isys, 0, 0, LTR_IWAKE_OFF); - mutex_lock(&iwake_watermark->mutex); set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX, CRITICAL_THRESHOLD_IWAKE_DISABLE); - goto unlock_exit; + return; } enable_iwake(isys, true); @@ -584,7 +507,8 @@ void update_watermark_setting(struct ipu6_isys *isys) did = calc_fill_time_us * DEFAULT_DID_RATIO / 100; ltr_did_type = LTR_ENHANNCE_IWAKE; } else { - get_lut_ltrdid(isys, <rdid); + ltrdid.lut_ltr.value = LTR_DEFAULT_VALUE; + ltrdid.lut_fill_time.value = FILL_TIME_DEFAULT_VALUE; if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th0) ltr = 0; @@ -608,7 +532,6 @@ void update_watermark_setting(struct ipu6_isys *isys) iwake_threshold = max_t(u32, 1, threshold_bytes >> shift); iwake_threshold = min_t(u32, iwake_threshold, max_sram_size); - mutex_lock(&iwake_watermark->mutex); if (isys->pdata->ipdata->enhanced_iwake) { set_iwake_register(isys, GDA_IWAKE_THRESHOLD_INDEX, DEFAULT_IWAKE_THRESHOLD); @@ -630,7 +553,7 @@ void update_watermark_setting(struct ipu6_isys *isys) iwake_critical_threshold = iwake_threshold + (IS_PIXEL_BUFFER_PAGES - iwake_threshold) / 2; - dev_dbg(dev, "threshold: %u critical: %u\n", iwake_threshold, + dev_dbg(dev, "watermark: threshold: %u critical: %u\n", iwake_threshold, iwake_critical_threshold); set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX, @@ -640,32 +563,6 @@ void update_watermark_setting(struct ipu6_isys *isys) isys->adev->isp->base + REG_PKGC_PMON_CFG); writel(VAL_PKGC_PMON_CFG_START, isys->adev->isp->base + REG_PKGC_PMON_CFG); -unlock_exit: - mutex_unlock(&iwake_watermark->mutex); -} - -static void isys_iwake_watermark_init(struct ipu6_isys *isys) -{ - struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark; - - INIT_LIST_HEAD(&iwake_watermark->video_list); - mutex_init(&iwake_watermark->mutex); - - iwake_watermark->ltrdid.lut_ltr.value = 0; - iwake_watermark->isys = isys; - iwake_watermark->iwake_enabled = false; - iwake_watermark->force_iwake_disable = false; -} - -static void isys_iwake_watermark_cleanup(struct ipu6_isys *isys) -{ - struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark; - - mutex_lock(&iwake_watermark->mutex); - list_del(&iwake_watermark->video_list); - mutex_unlock(&iwake_watermark->mutex); - - mutex_destroy(&iwake_watermark->mutex); } /* The .bound() notifier callback when a match is found */ @@ -736,6 +633,10 @@ static int isys_notifier_init(struct ipu6_isys *isys) continue; ret = v4l2_fwnode_endpoint_parse(ep, &vep); + if (ret && IS_IPU7(isp)) { + vep.bus_type = V4L2_MBUS_CSI2_CPHY; + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + } if (ret) { dev_err(dev, "fwnode endpoint parse failed: %d\n", ret); goto err_parse; @@ -751,6 +652,7 @@ static int isys_notifier_init(struct ipu6_isys *isys) s_asd->csi2.port = vep.base.port; s_asd->csi2.nlanes = vep.bus.mipi_csi2.num_data_lanes; + s_asd->csi2.bus_type = vep.bus_type; dev_dbg(dev, "remote endpoint port %d with %d lanes added\n", s_asd->csi2.port, s_asd->csi2.nlanes); @@ -789,7 +691,7 @@ static int isys_register_devices(struct ipu6_isys *isys) isys->media_dev.dev = dev; media_device_pci_init(&isys->media_dev, - pdev, IPU6_MEDIA_DEV_MODEL_NAME); + pdev, isys->adev->isp->model_name); strscpy(isys->v4l2_dev.name, isys->media_dev.model, sizeof(isys->v4l2_dev.name)); @@ -853,9 +755,10 @@ static void isys_unregister_devices(struct ipu6_isys *isys) static int isys_runtime_pm_resume(struct device *dev) { struct ipu6_bus_device *adev = to_ipu6_bus_device(dev); + const struct ipu6_fw_isys_ops *fw_ops = adev->auxdrv_data->fw_ops; struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); + const struct ipu6_isys_internal_pdata *ipdata = isys->pdata->ipdata; struct ipu6_device *isp = adev->isp; - unsigned long flags; int ret; ret = ipu6_mmu_hw_init(adev->mmu); @@ -866,49 +769,82 @@ static int isys_runtime_pm_resume(struct device *dev) ret = ipu6_buttress_start_tsc_sync(isp); if (ret) - return ret; + goto err_mmu_hw_cleanup; - spin_lock_irqsave(&isys->power_lock, flags); - isys->power = 1; - spin_unlock_irqrestore(&isys->power_lock, flags); + if (IS_IPU7(isp)) { + ipu7_isys_setup_hw(isys); + } else { + ipu6_isys_setup_hw(isys); + set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_ON); + } - isys_setup_hw(isys); + ipu6_configure_spc(adev->isp, &ipdata->hw_variant, + IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX, isys->pdata->base, + adev->pkg_dir, adev->pkg_dir_dma_addr); - set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_ON); + /* + * Buffers could have been left to wrong queue at last closure. + * Move them now back to empty buffer queue. + */ + ipu6_cleanup_fw_msg_bufs(isys); - return 0; + if (isys->fwctx) { + /* + * Something went wrong in previous shutdown. As we are now + * restarting isys we can safely delete old context. + */ + dev_warn(&adev->auxdev.dev, "clearing old context\n"); + fw_ops->cleanup(isys); + } + + ret = fw_ops->init(isys, ipdata->num_parallel_streams); + if (!ret) + return 0; + + isys->phy_termcal_val = 0; + cpu_latency_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); + + if (!IS_IPU7(isp)) + set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_OFF); + +err_mmu_hw_cleanup: + ipu6_mmu_hw_cleanup(adev->mmu); + + return ret; } static int isys_runtime_pm_suspend(struct device *dev) { struct ipu6_bus_device *adev = to_ipu6_bus_device(dev); struct ipu6_isys *isys = dev_get_drvdata(dev); - unsigned long flags; - - spin_lock_irqsave(&isys->power_lock, flags); - isys->power = 0; - spin_unlock_irqrestore(&isys->power_lock, flags); + struct ipu6_device *isp = adev->isp; + int ret = 0; - mutex_lock(&isys->mutex); - isys->need_reset = false; - mutex_unlock(&isys->mutex); + isys->adev->auxdrv_data->fw_ops->close(isys); + if (isys->fwctx) { + dev_warn(&isys->adev->auxdev.dev, "failed to close fw isys\n"); + ret = -EIO; + } isys->phy_termcal_val = 0; cpu_latency_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); - set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_OFF); + if (!IS_IPU7(isp)) + set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_OFF); ipu6_mmu_hw_cleanup(adev->mmu); - return 0; + return ret; } static int isys_suspend(struct device *dev) { struct ipu6_isys *isys = dev_get_drvdata(dev); + guard(mutex)(&isys->stream_mutex); + /* If stream is open, refuse to suspend */ - if (isys->stream_opened) + if (!ida_is_empty(&isys->streams)) return -EBUSY; return 0; @@ -1003,7 +939,7 @@ struct isys_fw_msgs *ipu6_get_fw_msg_buf(struct ipu6_isys_stream *stream) msg = list_last_entry(&isys->framebuflist, struct isys_fw_msgs, head); list_move(&msg->head, &isys->framebuflist_fw); spin_unlock_irqrestore(&isys->listlock, flags); - memset(&msg->fw_msg, 0, sizeof(msg->fw_msg)); + memset(&msg->ipu6, 0, sizeof(msg->ipu6)); return msg; } @@ -1019,30 +955,28 @@ void ipu6_cleanup_fw_msg_bufs(struct ipu6_isys *isys) spin_unlock_irqrestore(&isys->listlock, flags); } -void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, uintptr_t data) +void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, struct isys_fw_msgs *msg) { - struct isys_fw_msgs *msg; unsigned long flags; - void *ptr = (void *)data; - if (!ptr) + if (!msg) return; spin_lock_irqsave(&isys->listlock, flags); - msg = container_of(ptr, struct isys_fw_msgs, fw_msg.dummy); list_move(&msg->head, &isys->framebuflist); spin_unlock_irqrestore(&isys->listlock, flags); } +static const struct ipu6_auxdrv_data ipu6_isys_auxdrv_data; +static const struct ipu6_auxdrv_data ipu7_isys_auxdrv_data; + static int isys_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *auxdev_id) { const struct ipu6_isys_internal_csi2_pdata *csi2_pdata; struct ipu6_bus_device *adev = auxdev_to_adev(auxdev); struct ipu6_device *isp = adev->isp; - const struct firmware *fw; struct ipu6_isys *isys; - unsigned int i; int ret; if (!isp->bus_ready_to_probe) @@ -1052,8 +986,8 @@ static int isys_probe(struct auxiliary_device *auxdev, if (!isys) return -ENOMEM; - adev->auxdrv_data = - (const struct ipu6_auxdrv_data *)auxdev_id->driver_data; + adev->auxdrv_data = IS_IPU7(isp) ? &ipu7_isys_auxdrv_data : + &ipu6_isys_auxdrv_data; adev->auxdrv = to_auxiliary_drv(auxdev->dev.driver); isys->adev = adev; isys->pdata = adev->pdata; @@ -1068,8 +1002,6 @@ static int isys_probe(struct auxiliary_device *auxdev, isys->sensor_type = isys->pdata->ipdata->sensor_type_start; spin_lock_init(&isys->streams_lock); - spin_lock_init(&isys->power_lock); - isys->power = 0; isys->phy_termcal_val = 0; mutex_init(&isys->mutex); @@ -1083,18 +1015,7 @@ static int isys_probe(struct auxiliary_device *auxdev, dev_set_drvdata(&auxdev->dev, isys); - isys_stream_init(isys); - - if (!isp->secure_mode) { - fw = isp->cpd_fw; - ret = ipu6_buttress_map_fw_image(adev, fw, &adev->fw_sgt); - if (ret) - goto release_firmware; - - ret = ipu6_cpd_create_pkg_dir(adev, isp->cpd_fw->data); - if (ret) - goto remove_shared_buffer; - } + ida_init(&isys->streams); cpu_latency_qos_add_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); @@ -1102,11 +1023,11 @@ static int isys_probe(struct auxiliary_device *auxdev, if (ret < 0) goto out_remove_pkg_dir_shared_buffer; - isys_iwake_watermark_init(isys); - - if (is_ipu6se(adev->isp->hw_ver)) + if (IS_IPU7(adev->isp)) + isys->phy_set_power = ipu7_isys_csi_phy_set_power; + else if (IS_IPU6SE(adev->isp)) isys->phy_set_power = ipu6_isys_jsl_phy_set_power; - else if (is_ipu6ep_mtl(adev->isp->hw_ver)) + else if (IS_IPU6EP_MTL(adev->isp)) isys->phy_set_power = ipu6_isys_dwc_phy_set_power; else isys->phy_set_power = ipu6_isys_mcd_phy_set_power; @@ -1121,17 +1042,6 @@ free_fw_msg_bufs: free_fw_msg_bufs(isys); out_remove_pkg_dir_shared_buffer: cpu_latency_qos_remove_request(&isys->pm_qos); - if (!isp->secure_mode) - ipu6_cpd_free_pkg_dir(adev); -remove_shared_buffer: - if (!isp->secure_mode) - ipu6_buttress_unmap_fw_image(adev, &adev->fw_sgt); -release_firmware: - if (!isp->secure_mode) - release_firmware(adev->fw); - - for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) - mutex_destroy(&isys->streams[i].mutex); mutex_destroy(&isys->mutex); mutex_destroy(&isys->stream_mutex); @@ -1141,10 +1051,9 @@ release_firmware: static void isys_remove(struct auxiliary_device *auxdev) { - struct ipu6_bus_device *adev = auxdev_to_adev(auxdev); struct ipu6_isys *isys = dev_get_drvdata(&auxdev->dev); - struct ipu6_device *isp = adev->isp; - unsigned int i; + + ida_destroy(&isys->streams); free_fw_msg_bufs(isys); @@ -1153,191 +1062,27 @@ static void isys_remove(struct auxiliary_device *auxdev) cpu_latency_qos_remove_request(&isys->pm_qos); - if (!isp->secure_mode) { - ipu6_cpd_free_pkg_dir(adev); - ipu6_buttress_unmap_fw_image(adev, &adev->fw_sgt); - release_firmware(adev->fw); - } - - for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) - mutex_destroy(&isys->streams[i].mutex); - - isys_iwake_watermark_cleanup(isys); mutex_destroy(&isys->stream_mutex); mutex_destroy(&isys->mutex); } -struct fwmsg { - int type; - char *msg; - bool valid_ts; -}; - -static const struct fwmsg fw_msg[] = { - {IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE, "STREAM_OPEN_DONE", 0}, - {IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK, "STREAM_CLOSE_ACK", 0}, - {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK, "STREAM_START_ACK", 0}, - {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK, - "STREAM_START_AND_CAPTURE_ACK", 0}, - {IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK, "STREAM_STOP_ACK", 0}, - {IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK, "STREAM_FLUSH_ACK", 0}, - {IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY, "PIN_DATA_READY", 1}, - {IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK, "STREAM_CAPTURE_ACK", 0}, - {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE, - "STREAM_START_AND_CAPTURE_DONE", 1}, - {IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE, "STREAM_CAPTURE_DONE", 1}, - {IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF, "FRAME_SOF", 1}, - {IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF, "FRAME_EOF", 1}, - {IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY, "STATS_READY", 1}, - {-1, "UNKNOWN MESSAGE", 0} +static const struct ipu6_auxdrv_data ipu6_isys_auxdrv_data = { + .isr = ipu6_isys_isr, + .isr_threaded = NULL, + .wake_isr_thread = false, + .fw_ops = &ipu6_fw_isys_ops, }; -static u32 resp_type_to_index(int type) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(fw_msg); i++) - if (fw_msg[i].type == type) - return i; - - return ARRAY_SIZE(fw_msg) - 1; -} - -static int isys_isr_one(struct ipu6_bus_device *adev) -{ - struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); - struct ipu6_fw_isys_resp_info_abi *resp; - struct ipu6_isys_stream *stream; - struct ipu6_isys_csi2 *csi2 = NULL; - u32 index; - u64 ts; - - if (!isys->fwcom) - return 1; - - resp = ipu6_fw_isys_get_resp(isys->fwcom, IPU6_BASE_MSG_RECV_QUEUES); - if (!resp) - return 1; - - ts = (u64)resp->timestamp[1] << 32 | resp->timestamp[0]; - - index = resp_type_to_index(resp->type); - dev_dbg(&adev->auxdev.dev, - "FW resp %02d %s, stream %u, ts 0x%16.16llx, pin %d\n", - resp->type, fw_msg[index].msg, resp->stream_handle, - fw_msg[index].valid_ts ? ts : 0, resp->pin_id); - - if (resp->error_info.error == IPU6_FW_ISYS_ERROR_STREAM_IN_SUSPENSION) - /* Suspension is kind of special case: not enough buffers */ - dev_dbg(&adev->auxdev.dev, - "FW error resp SUSPENSION, details %d\n", - resp->error_info.error_details); - else if (resp->error_info.error) - dev_dbg(&adev->auxdev.dev, - "FW error resp error %d, details %d\n", - resp->error_info.error, resp->error_info.error_details); - - if (resp->stream_handle >= IPU6_ISYS_MAX_STREAMS) { - dev_err(&adev->auxdev.dev, "bad stream handle %u\n", - resp->stream_handle); - goto leave; - } - - stream = ipu6_isys_query_stream_by_handle(isys, resp->stream_handle); - if (!stream) { - dev_err(&adev->auxdev.dev, "stream of stream_handle %u is unused\n", - resp->stream_handle); - goto leave; - } - stream->error = resp->error_info.error; - - csi2 = ipu6_isys_subdev_to_csi2(stream->asd); - - switch (resp->type) { - case IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE: - complete(&stream->stream_open_completion); - break; - case IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK: - complete(&stream->stream_close_completion); - break; - case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK: - complete(&stream->stream_start_completion); - break; - case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK: - complete(&stream->stream_start_completion); - break; - case IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK: - complete(&stream->stream_stop_completion); - break; - case IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK: - complete(&stream->stream_stop_completion); - break; - case IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY: - /* - * firmware only release the capture msg until software - * get pin_data_ready event - */ - ipu6_put_fw_msg_buf(ipu6_bus_get_drvdata(adev), resp->buf_id); - if (resp->pin_id < IPU6_ISYS_OUTPUT_PINS && - stream->output_pins_queue[resp->pin_id]) - ipu6_isys_queue_buf_ready(stream, resp); - else - dev_warn(&adev->auxdev.dev, - "%d:No queue for pin id %d\n", - resp->stream_handle, resp->pin_id); - if (csi2) - ipu6_isys_csi2_error(csi2); - - break; - case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK: - break; - case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE: - case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE: - break; - case IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF: - - ipu6_isys_csi2_sof_event_by_stream(stream); - stream->seq[stream->seq_index].sequence = - atomic_read(&stream->sequence) - 1; - stream->seq[stream->seq_index].timestamp = ts; - dev_dbg(&adev->auxdev.dev, - "sof: handle %d: (index %u), timestamp 0x%16.16llx\n", - resp->stream_handle, - stream->seq[stream->seq_index].sequence, ts); - stream->seq_index = (stream->seq_index + 1) - % IPU6_ISYS_MAX_PARALLEL_SOF; - break; - case IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF: - ipu6_isys_csi2_eof_event_by_stream(stream); - dev_dbg(&adev->auxdev.dev, - "eof: handle %d: (index %u), timestamp 0x%16.16llx\n", - resp->stream_handle, - stream->seq[stream->seq_index].sequence, ts); - break; - case IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY: - break; - default: - dev_err(&adev->auxdev.dev, "%d:unknown response type %u\n", - resp->stream_handle, resp->type); - break; - } - - ipu6_isys_put_stream(stream); -leave: - ipu6_fw_isys_put_resp(isys->fwcom, IPU6_BASE_MSG_RECV_QUEUES); - return 0; -} - -static const struct ipu6_auxdrv_data ipu6_isys_auxdrv_data = { - .isr = isys_isr, +static const struct ipu6_auxdrv_data ipu7_isys_auxdrv_data = { + .isr = ipu7_isys_isr, .isr_threaded = NULL, .wake_isr_thread = false, + .fw_ops = &ipu7_fw_isys_ops, }; static const struct auxiliary_device_id ipu6_isys_id_table[] = { { .name = "intel_ipu6.isys", - .driver_data = (kernel_ulong_t)&ipu6_isys_auxdrv_data, }, { } }; diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.h b/drivers/media/pci/intel/ipu6/ipu6-isys.h index 7fb8cb820912..2af20f56a965 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys.h +++ b/drivers/media/pci/intel/ipu6/ipu6-isys.h @@ -4,6 +4,7 @@ #ifndef IPU6_ISYS_H #define IPU6_ISYS_H +#include <linux/idr.h> #include <linux/irqreturn.h> #include <linux/list.h> #include <linux/mutex.h> @@ -14,9 +15,11 @@ #include <media/media-device.h> #include <media/v4l2-async.h> #include <media/v4l2-device.h> +#include <media/v4l2-mediabus.h> #include "ipu6.h" #include "ipu6-fw-isys.h" +#include "ipu7-fw-isys.h" #include "ipu6-isys-csi2.h" #include "ipu6-isys-video.h" @@ -60,41 +63,10 @@ struct ipu6_bus_device; #define IPU6EP_MTL_LTR_VALUE 1023 #define IPU6EP_MTL_MIN_MEMOPEN_TH 0xc -struct ltr_did { - union { - u32 value; - struct { - u8 val0; - u8 val1; - u8 val2; - u8 val3; - } bits; - } lut_ltr; - union { - u32 value; - struct { - u8 th0; - u8 th1; - u8 th2; - u8 th3; - } bits; - } lut_fill_time; -}; - -struct isys_iwake_watermark { - bool iwake_enabled; - bool force_iwake_disable; - u32 iwake_threshold; - u64 isys_pixelbuffer_datarate; - struct ltr_did ltrdid; - struct mutex mutex; /* protect whole struct */ - struct ipu6_isys *isys; - struct list_head video_list; -}; - struct ipu6_isys_csi2_config { u32 nlanes; u32 port; + enum v4l2_mbus_type bus_type; }; struct sensor_async_sd { @@ -108,17 +80,12 @@ struct sensor_async_sd { * @media_dev: Media device * @v4l2_dev: V4L2 device * @adev: ISYS bus device - * @power: Is ISYS powered on or not? - * @isr_bits: Which bits does the ISR handle? - * @power_lock: Serialise access to power (power state in general) * @csi2_rx_ctrl_cached: cached shared value between all CSI2 receivers * @streams_lock: serialise access to streams * @streams: streams per firmware stream ID - * @fwcom: fw communication layer private pointer - * or optional external library private pointer + * @fwctx: fw communication layer context pointer * @phy_termcal_val: the termination calibration value, only used for DWC PHY * @need_reset: Isys requires d0i0->i3 transition - * @ref_count: total number of callers fw open * @mutex: serialise access isys video open/release related operations * @stream_mutex: serialise stream start and stop, queueing requests * @pdata: platform data pointer @@ -129,20 +96,17 @@ struct ipu6_isys { struct v4l2_device v4l2_dev; struct ipu6_bus_device *adev; - int power; - spinlock_t power_lock; u32 isr_csi2_bits; u32 csi2_rx_ctrl_cached; spinlock_t streams_lock; - struct ipu6_isys_stream streams[IPU6_ISYS_MAX_STREAMS]; - int streams_ref_count[IPU6_ISYS_MAX_STREAMS]; - void *fwcom; + struct ipu6_isys_stream *streams_by_handle[IPU6_ISYS_MAX_STREAMS]; + void *fwctx; u32 phy_termcal_val; + u32 phy_rext_cal; bool need_reset; bool icache_prefetch; bool csi2_cse_ipc_not_supported; - unsigned int ref_count; - unsigned int stream_opened; + bool iwake_watermark_enabled; unsigned int sensor_type; struct mutex mutex; @@ -162,26 +126,65 @@ struct ipu6_isys { struct list_head framebuflist; struct list_head framebuflist_fw; struct v4l2_async_notifier notifier; - struct isys_iwake_watermark iwake_watermark; + struct ida streams; }; struct isys_fw_msgs { union { u64 dummy; - struct ipu6_fw_isys_frame_buff_set_abi frame; - struct ipu6_fw_isys_stream_cfg_data_abi stream; - } fw_msg; + union { + struct ipu6_fw_isys_frame_buff_set_abi frame; + struct ipu6_fw_isys_stream_cfg_data_abi stream; + } ipu6; + union { + struct ipu7_fw_isys_frame_buff_set frame; + struct ipu7_fw_isys_stream_cfg stream; + } ipu7; + }; struct list_head head; dma_addr_t dma_addr; }; +struct ipu6_fw_isys_ops { + int (*init)(struct ipu6_isys *isys, unsigned int num_streams); + int (*close)(struct ipu6_isys *isys); + int (*send_cmd)(struct ipu6_isys *isys, + const unsigned int stream_handle, + void *cpu_mapped_buf, + dma_addr_t dma_mapped_buf, + size_t size, u16 send_type); + void (*cleanup)(struct ipu6_isys *isys); + int (*prepare_stream_cfg)(struct ipu6_isys_stream *stream, + struct v4l2_mbus_frame_desc *desc, + struct isys_fw_msgs *msg); + void (*prepare_buf_set)(struct isys_fw_msgs *msg, + struct ipu6_isys_stream *stream, + struct ipu6_isys_buffer_list *bl); + int (*stream_open)(struct ipu6_isys *isys, + const unsigned int stream_handle, + struct isys_fw_msgs *msg); + int (*stream_start)(struct ipu6_isys *isys, + const unsigned int stream_handle, + struct isys_fw_msgs *msg, bool capture); + int (*stream_capture)(struct ipu6_isys *isys, + const unsigned int stream_handle, + struct isys_fw_msgs *msg); + int (*stream_flush)(struct ipu6_isys *isys, + const unsigned int stream_handle); + int (*stream_close)(struct ipu6_isys *isys, + const unsigned int stream_handle); + void (*dump_stream_cfg)(struct device *dev, struct isys_fw_msgs *msg); + void (*dump_frame_buf_set)(struct device *dev, struct isys_fw_msgs *msg, + unsigned int outputs); +}; + struct isys_fw_msgs *ipu6_get_fw_msg_buf(struct ipu6_isys_stream *stream); -void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, uintptr_t data); +void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, struct isys_fw_msgs *msg); void ipu6_cleanup_fw_msg_bufs(struct ipu6_isys *isys); extern const struct v4l2_ioctl_ops ipu6_isys_ioctl_ops; -void update_watermark_setting(struct ipu6_isys *isys); +void ipu6_isys_update_watermark_setting(struct ipu6_isys *isys); int ipu6_isys_mcd_phy_set_power(struct ipu6_isys *isys, struct ipu6_isys_csi2_config *cfg, diff --git a/drivers/media/pci/intel/ipu6/ipu6-mmu-hw.c b/drivers/media/pci/intel/ipu6/ipu6-mmu-hw.c new file mode 100644 index 000000000000..2b395fbd5969 --- /dev/null +++ b/drivers/media/pci/intel/ipu6/ipu6-mmu-hw.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Intel Corporation + */ +#include <asm/barrier.h> + +#include <linux/bits.h> +#include <linux/gfp.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#include "ipu6.h" +#include "ipu6-dma.h" +#include "ipu6-mmu.h" +#include "ipu6-platform-regs.h" + +#define ISP_PAGE_SHIFT 12 +#define ISP_PAGE_SIZE BIT(ISP_PAGE_SHIFT) +#define ISP_PAGE_MASK (~(ISP_PAGE_SIZE - 1)) + +#define ISP_L1PT_SHIFT 22 +#define ISP_L1PT_MASK (~((1U << ISP_L1PT_SHIFT) - 1)) + +#define ISP_L2PT_SHIFT 12 +#define ISP_L2PT_MASK (~(ISP_L1PT_MASK | (~(ISP_PAGE_MASK)))) + +#define ISP_L1PT_PTES 1024 +#define ISP_L2PT_PTES 1024 + +#define ISP_PADDR_SHIFT 12 + +#define REG_TLB_INVALIDATE 0x0000 + +#define REG_L1_PHYS 0x0004 /* 27-bit pfn */ +#define REG_INFO 0x0008 + +#define TBL_PHYS_ADDR(a) ((phys_addr_t)(a) << ISP_PADDR_SHIFT) + +static struct ipu6_mmu_hw ipu6_isys_mmu_hwdata[] = { + { + .offset = IPU6_ISYS_IOMMU0_OFFSET, + .info_bits = IPU6_INFO_REQUEST_DESTINATION_IOSF, + .nr_l1streams = 16, + .l1_block_sz = { + 3, 8, 2, 2, 2, 2, 2, 2, 1, 1, + 1, 1, 1, 1, 1, 1 + }, + .nr_l2streams = 16, + .l2_block_sz = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2 + }, + .insert_read_before_invalidate = false, + .l1_stream_id_reg_offset = + IPU6_MMU_L1_STREAM_ID_REG_OFFSET, + .l2_stream_id_reg_offset = + IPU6_MMU_L2_STREAM_ID_REG_OFFSET, + }, + { + .offset = IPU6_ISYS_IOMMU1_OFFSET, + .info_bits = 0, + .nr_l1streams = 16, + .l1_block_sz = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 1, 1, 4 + }, + .nr_l2streams = 16, + .l2_block_sz = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2 + }, + .insert_read_before_invalidate = false, + .l1_stream_id_reg_offset = + IPU6_MMU_L1_STREAM_ID_REG_OFFSET, + .l2_stream_id_reg_offset = + IPU6_MMU_L2_STREAM_ID_REG_OFFSET, + }, + { + .offset = IPU6_ISYS_IOMMUI_OFFSET, + .info_bits = 0, + .nr_l1streams = 0, + .nr_l2streams = 0, + .insert_read_before_invalidate = false, + }, +}; + +static struct ipu6_mmu_hw ipu6_psys_mmu_hwdata[] = { + { + .offset = IPU6_PSYS_IOMMU0_OFFSET, + .info_bits = + IPU6_INFO_REQUEST_DESTINATION_IOSF, + .nr_l1streams = 16, + .l1_block_sz = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2 + }, + .nr_l2streams = 16, + .l2_block_sz = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2 + }, + .insert_read_before_invalidate = false, + .l1_stream_id_reg_offset = + IPU6_MMU_L1_STREAM_ID_REG_OFFSET, + .l2_stream_id_reg_offset = + IPU6_MMU_L2_STREAM_ID_REG_OFFSET, + }, + { + .offset = IPU6_PSYS_IOMMU1_OFFSET, + .info_bits = 0, + .nr_l1streams = 32, + .l1_block_sz = { + 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 10, + 5, 4, 14, 6, 4, 14, 6, 4, 8, + 4, 2, 1, 1, 1, 1, 14 + }, + .nr_l2streams = 32, + .l2_block_sz = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2 + }, + .insert_read_before_invalidate = false, + .l1_stream_id_reg_offset = + IPU6_MMU_L1_STREAM_ID_REG_OFFSET, + .l2_stream_id_reg_offset = + IPU6_PSYS_MMU1W_L2_STREAM_ID_REG_OFFSET, + }, + { + .offset = IPU6_PSYS_IOMMU1R_OFFSET, + .info_bits = 0, + .nr_l1streams = 16, + .l1_block_sz = { + 1, 4, 4, 4, 4, 16, 8, 4, 32, + 16, 16, 2, 2, 2, 1, 12 + }, + .nr_l2streams = 16, + .l2_block_sz = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2 + }, + .insert_read_before_invalidate = false, + .l1_stream_id_reg_offset = + IPU6_MMU_L1_STREAM_ID_REG_OFFSET, + .l2_stream_id_reg_offset = + IPU6_MMU_L2_STREAM_ID_REG_OFFSET, + }, + { + .offset = IPU6_PSYS_IOMMUI_OFFSET, + .info_bits = 0, + .nr_l1streams = 0, + .nr_l2streams = 0, + .insert_read_before_invalidate = false, + }, +}; + +struct ipu6_mmu_hwdata { + struct ipu6_mmu_hw *hwdata; + unsigned int nr_mmus; +}; + +static const struct ipu6_mmu_hwdata ipu6_mmu_hwdata_lookup[IPU_SUBSYS_NUM] = { + [IPU_PSYS] = { + .hwdata = ipu6_psys_mmu_hwdata, + .nr_mmus = ARRAY_SIZE(ipu6_psys_mmu_hwdata), + }, + [IPU_ISYS] = { + .hwdata = ipu6_isys_mmu_hwdata, + .nr_mmus = ARRAY_SIZE(ipu6_isys_mmu_hwdata), + }, +}; + +static void __ipu6_tlb_invalidate(struct ipu6_mmu *mmu) +{ + struct ipu6_mmu_hw *mmu_hw = mmu->ipu6_mmu_hw; + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&mmu->ready_lock, flags); + if (!mmu->ready) { + spin_unlock_irqrestore(&mmu->ready_lock, flags); + return; + } + + for (i = 0; i < mmu->nr_mmus; i++) { + /* + * To avoid the HW bug induced dead lock in some of the IPU6 + * MMUs on successive invalidate calls, we need to first do a + * read to the page table base before writing the invalidate + * register. MMUs which need to implement this WA, will have + * the insert_read_before_invalidate flags set as true. + * Disregard the return value of the read. + */ + if (mmu_hw[i].insert_read_before_invalidate) + readl(mmu_hw[i].base + REG_L1_PHYS); + + writel(0xffffffff, mmu_hw[i].base + REG_TLB_INVALIDATE); + /* + * The TLB invalidation is a "single cycle" (IOMMU clock cycles) + * When the actual MMIO write reaches the IPU6 TLB Invalidate + * register, wmb() will force the TLB invalidate out if the CPU + * attempts to update the IOMMU page table (or sooner). + */ + wmb(); + } + spin_unlock_irqrestore(&mmu->ready_lock, flags); +} + +static int __ipu6_mmu_hw_init(struct ipu6_mmu *mmu) +{ + struct ipu6_mmu_info *mmu_info = mmu->dmap->mmu_info; + struct ipu6_mmu_hw *mmu_hw = mmu->ipu6_mmu_hw; + + /* Initialise the each MMU HW block */ + for (unsigned int i = 0; i < mmu->nr_mmus; i++) { + unsigned int j; + u16 block_addr; + + /* Write page table address per MMU */ + writel((phys_addr_t)mmu_info->l1_pt_dma, + mmu_hw[i].base + REG_L1_PHYS); + + /* Set info bits per MMU */ + writel(mmu_hw[i].info_bits, mmu_hw[i].base + REG_INFO); + + /* Configure MMU TLB stream configuration for L1 */ + for (j = 0, block_addr = 0; j < mmu_hw[i].nr_l1streams; + block_addr += mmu_hw[i].l1_block_sz[j], j++) { + if (block_addr > IPU6_MAX_LI_BLOCK_ADDR) { + dev_err(mmu->dev, "invalid L1 configuration\n"); + return -EINVAL; + } + + /* Write block start address for each streams */ + writel(block_addr, mmu_hw[i].base + + mmu_hw[i].l1_stream_id_reg_offset + 4 * j); + } + + /* Configure MMU TLB stream configuration for L2 */ + for (j = 0, block_addr = 0; j < mmu_hw[i].nr_l2streams; + block_addr += mmu_hw[i].l2_block_sz[j], j++) { + if (block_addr > IPU6_MAX_L2_BLOCK_ADDR) { + dev_err(mmu->dev, "invalid L2 configuration\n"); + return -EINVAL; + } + + writel(block_addr, mmu_hw[i].base + + mmu_hw[i].l2_stream_id_reg_offset + 4 * j); + } + } + + return 0; +} + +static int __ipu6_mmu_init_hw_data(struct ipu6_mmu *mmu, struct device *dev, + void __iomem *base) +{ + const struct ipu6_mmu_hwdata *lookup; + struct ipu6_mmu_hw *mmu_hw, *src; + unsigned int i, nr_mmus; + + if (mmu->mmid >= IPU_SUBSYS_NUM) + return -EINVAL; + + lookup = &ipu6_mmu_hwdata_lookup[mmu->mmid]; + src = lookup->hwdata; + nr_mmus = lookup->nr_mmus; + + mmu_hw = devm_kcalloc(dev, nr_mmus, sizeof(*mmu_hw), GFP_KERNEL); + if (!mmu_hw) + return -ENOMEM; + + for (i = 0; i < nr_mmus; i++) { + if (src[i].nr_l1streams > IPU6_MMU_MAX_TLB_L1_STREAMS || + src[i].nr_l2streams > IPU6_MMU_MAX_TLB_L2_STREAMS) + return -EINVAL; + + mmu_hw[i] = src[i]; + mmu_hw[i].base = base + src[i].offset; + } + + mmu->nr_mmus = nr_mmus; + mmu->ipu6_mmu_hw = mmu_hw; + + return 0; +} + +const struct ipu6_mmu_hw_ops ipu6_mmu_ops = { + .init_hw_data = __ipu6_mmu_init_hw_data, + .hw_init = __ipu6_mmu_hw_init, + .tlb_invalidate = __ipu6_tlb_invalidate, +}; diff --git a/drivers/media/pci/intel/ipu6/ipu6-mmu.c b/drivers/media/pci/intel/ipu6/ipu6-mmu.c index 0e6dca36b6ba..243d438786ba 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-mmu.c +++ b/drivers/media/pci/intel/ipu6/ipu6-mmu.c @@ -5,7 +5,6 @@ #include <asm/barrier.h> #include <linux/align.h> -#include <linux/atomic.h> #include <linux/bitops.h> #include <linux/bits.h> #include <linux/bug.h> @@ -13,10 +12,8 @@ #include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/gfp.h> -#include <linux/io.h> #include <linux/iova.h> #include <linux/math.h> -#include <linux/minmax.h> #include <linux/mm.h> #include <linux/pfn.h> #include <linux/slab.h> @@ -51,42 +48,6 @@ #define TBL_PHYS_ADDR(a) ((phys_addr_t)(a) << ISP_PADDR_SHIFT) -static void tlb_invalidate(struct ipu6_mmu *mmu) -{ - unsigned long flags; - unsigned int i; - - spin_lock_irqsave(&mmu->ready_lock, flags); - if (!mmu->ready) { - spin_unlock_irqrestore(&mmu->ready_lock, flags); - return; - } - - for (i = 0; i < mmu->nr_mmus; i++) { - /* - * To avoid the HW bug induced dead lock in some of the IPU6 - * MMUs on successive invalidate calls, we need to first do a - * read to the page table base before writing the invalidate - * register. MMUs which need to implement this WA, will have - * the insert_read_before_invalidate flags set as true. - * Disregard the return value of the read. - */ - if (mmu->mmu_hw[i].insert_read_before_invalidate) - readl(mmu->mmu_hw[i].base + REG_L1_PHYS); - - writel(0xffffffff, mmu->mmu_hw[i].base + - REG_TLB_INVALIDATE); - /* - * The TLB invalidation is a "single cycle" (IOMMU clock cycles) - * When the actual MMIO write reaches the IPU6 TLB Invalidate - * register, wmb() will force the TLB invalidate out if the CPU - * attempts to update the IOMMU page table (or sooner). - */ - wmb(); - } - spin_unlock_irqrestore(&mmu->ready_lock, flags); -} - #ifdef DEBUG static void page_table_dump(struct ipu6_mmu_info *mmu_info) { @@ -472,55 +433,14 @@ out_free_iova: int ipu6_mmu_hw_init(struct ipu6_mmu *mmu) { - struct ipu6_mmu_info *mmu_info; unsigned long flags; - unsigned int i; - - mmu_info = mmu->dmap->mmu_info; - - /* Initialise the each MMU HW block */ - for (i = 0; i < mmu->nr_mmus; i++) { - struct ipu6_mmu_hw *mmu_hw = &mmu->mmu_hw[i]; - unsigned int j; - u16 block_addr; - - /* Write page table address per MMU */ - writel((phys_addr_t)mmu_info->l1_pt_dma, - mmu->mmu_hw[i].base + REG_L1_PHYS); - - /* Set info bits per MMU */ - writel(mmu->mmu_hw[i].info_bits, - mmu->mmu_hw[i].base + REG_INFO); - - /* Configure MMU TLB stream configuration for L1 */ - for (j = 0, block_addr = 0; j < mmu_hw->nr_l1streams; - block_addr += mmu->mmu_hw[i].l1_block_sz[j], j++) { - if (block_addr > IPU6_MAX_LI_BLOCK_ADDR) { - dev_err(mmu->dev, "invalid L1 configuration\n"); - return -EINVAL; - } - - /* Write block start address for each streams */ - writel(block_addr, mmu_hw->base + - mmu_hw->l1_stream_id_reg_offset + 4 * j); - } - - /* Configure MMU TLB stream configuration for L2 */ - for (j = 0, block_addr = 0; j < mmu_hw->nr_l2streams; - block_addr += mmu->mmu_hw[i].l2_block_sz[j], j++) { - if (block_addr > IPU6_MAX_L2_BLOCK_ADDR) { - dev_err(mmu->dev, "invalid L2 configuration\n"); - return -EINVAL; - } + int ret; - writel(block_addr, mmu_hw->base + - mmu_hw->l2_stream_id_reg_offset + 4 * j); - } - } + ret = mmu->ops->hw_init(mmu); + if (ret) + return ret; if (!mmu->trash_page) { - int ret; - mmu->trash_page = alloc_page(GFP_KERNEL); if (!mmu->trash_page) { dev_err(mmu->dev, "insufficient memory for trash buffer\n"); @@ -553,7 +473,12 @@ static struct ipu6_mmu_info *ipu6_mmu_alloc(struct ipu6_device *isp) if (!mmu_info) return NULL; - mmu_info->aperture_start = 0; + if (IS_IPU7(isp)) + mmu_info->aperture_start = isp->secure_mode ? + IPU7_FW_CODE_REGION_END : IPU7_FW_CODE_REGION_START; + else + mmu_info->aperture_start = 0; + mmu_info->aperture_end = (dma_addr_t)DMA_BIT_MASK(isp->secure_mode ? IPU6_MMU_ADDR_BITS : @@ -612,6 +537,7 @@ EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_cleanup, "INTEL_IPU6"); static struct ipu6_dma_mapping *alloc_dma_mapping(struct ipu6_device *isp) { struct ipu6_dma_mapping *dmap; + unsigned long base_pfn; dmap = kzalloc_obj(*dmap); if (!dmap) @@ -623,7 +549,9 @@ static struct ipu6_dma_mapping *alloc_dma_mapping(struct ipu6_device *isp) return NULL; } - init_iova_domain(&dmap->iovad, SZ_4K, 1); + base_pfn = max_t(unsigned long, 1, + PFN_DOWN(dmap->mmu_info->aperture_start)); + init_iova_domain(&dmap->iovad, SZ_4K, base_pfn); dmap->mmu_info->dmap = dmap; dev_dbg(&isp->pdev->dev, "alloc mapping\n"); @@ -746,45 +674,26 @@ static void ipu6_mmu_destroy(struct ipu6_mmu *mmu) } struct ipu6_mmu *ipu6_mmu_init(struct device *dev, - void __iomem *base, int mmid, - const struct ipu6_hw_variants *hw) + void __iomem *base, int mmid) { struct ipu6_device *isp = pci_get_drvdata(to_pci_dev(dev)); - struct ipu6_mmu_pdata *pdata; struct ipu6_mmu *mmu; - unsigned int i; - - if (hw->nr_mmus > IPU6_MMU_MAX_DEVICES) - return ERR_PTR(-EINVAL); - - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return ERR_PTR(-ENOMEM); - - for (i = 0; i < hw->nr_mmus; i++) { - struct ipu6_mmu_hw *pdata_mmu = &pdata->mmu_hw[i]; - const struct ipu6_mmu_hw *src_mmu = &hw->mmu_hw[i]; - - if (src_mmu->nr_l1streams > IPU6_MMU_MAX_TLB_L1_STREAMS || - src_mmu->nr_l2streams > IPU6_MMU_MAX_TLB_L2_STREAMS) - return ERR_PTR(-EINVAL); - - *pdata_mmu = *src_mmu; - pdata_mmu->base = base + src_mmu->offset; - } + int ret; mmu = devm_kzalloc(dev, sizeof(*mmu), GFP_KERNEL); if (!mmu) return ERR_PTR(-ENOMEM); + mmu->ops = IS_IPU7(isp) ? &ipu7_mmu_ops : &ipu6_mmu_ops; mmu->mmid = mmid; - mmu->mmu_hw = pdata->mmu_hw; - mmu->nr_mmus = hw->nr_mmus; - mmu->tlb_invalidate = tlb_invalidate; mmu->ready = false; INIT_LIST_HEAD(&mmu->vma_list); spin_lock_init(&mmu->ready_lock); + ret = mmu->ops->init_hw_data(mmu, dev, base); + if (ret) + return ERR_PTR(ret); + mmu->dmap = alloc_dma_mapping(isp); if (!mmu->dmap) { dev_err(dev, "can't alloc dma mapping\n"); diff --git a/drivers/media/pci/intel/ipu6/ipu6-mmu.h b/drivers/media/pci/intel/ipu6/ipu6-mmu.h index 8e40b4a66d7d..44880478d242 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-mmu.h +++ b/drivers/media/pci/intel/ipu6/ipu6-mmu.h @@ -1,19 +1,17 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/* Copyright (C) 2013--2024 Intel Corporation */ +/* Copyright (C) 2013--2026 Intel Corporation */ #ifndef IPU6_MMU_H #define IPU6_MMU_H -#define ISYS_MMID 1 -#define PSYS_MMID 0 - #include <linux/list.h> #include <linux/spinlock_types.h> #include <linux/types.h> +#include "ipu7-mmu-hw.h" + struct device; struct page; -struct ipu6_hw_variants; struct ipu6_mmu_info { struct device *dev; @@ -35,10 +33,150 @@ struct ipu6_mmu_info { struct ipu6_dma_mapping *dmap; }; +/* + * MMU Invalidation HW bug workaround by ZLW mechanism + * + * Old IPU6 MMUV2 has a bug in the invalidation mechanism which might result in + * wrong translation or replication of the translation. This will cause data + * corruption. So we cannot directly use the MMU V2 invalidation registers + * to invalidate the MMU. Instead, whenever an invalidate is called, we need to + * clear the TLB by evicting all the valid translations by filling it with trash + * buffer (which is guaranteed not to be used by any other processes). ZLW is + * used to fill the L1 and L2 caches with the trash buffer translations. ZLW + * or Zero length write, is pre-fetch mechanism to pre-fetch the pages in + * advance to the L1 and L2 caches without triggering any memory operations. + * + * In MMU V2, L1 -> 16 streams and 64 blocks, maximum 16 blocks per stream + * One L1 block has 16 entries, hence points to 16 * 4K pages + * L2 -> 16 streams and 32 blocks. 2 blocks per streams + * One L2 block maps to 1024 L1 entries, hence points to 4MB address range + * 2 blocks per L2 stream means, 1 stream points to 8MB range + * + * As we need to clear the caches and 8MB being the biggest cache size, we need + * to have trash buffer which points to 8MB address range. As these trash + * buffers are not used for any memory transactions, we need only the least + * amount of physical memory. So we reserve 8MB IOVA address range but only + * one page is reserved from physical memory. Each of this 8MB IOVA address + * range is then mapped to the same physical memory page. + */ +/* One L2 entry maps 1024 L1 entries and one L1 entry per page */ +#define IPU6_MMUV2_L2_RANGE (1024 * PAGE_SIZE) +/* Max L2 blocks per stream */ +#define IPU6_MMUV2_MAX_L2_BLOCKS 2 +/* Max L1 blocks per stream */ +#define IPU6_MMUV2_MAX_L1_BLOCKS 16 +#define IPU6_MMUV2_TRASH_RANGE (IPU6_MMUV2_L2_RANGE * IPU6_MMUV2_MAX_L2_BLOCKS) +/* Entries per L1 block */ +#define MMUV2_ENTRIES_PER_L1_BLOCK 16 +#define MMUV2_TRASH_L1_BLOCK_OFFSET (MMUV2_ENTRIES_PER_L1_BLOCK * PAGE_SIZE) +#define MMUV2_TRASH_L2_BLOCK_OFFSET IPU6_MMUV2_L2_RANGE + +/* + * In some of the IPU6 MMUs, there is provision to configure L1 and L2 page + * table caches. Both these L1 and L2 caches are divided into multiple sections + * called streams. There is maximum 16 streams for both caches. Each of these + * sections are subdivided into multiple blocks. When nr_l1streams = 0 and + * nr_l2streams = 0, means the MMU is of type MMU_V1 and do not support + * L1/L2 page table caches. + * + * L1 stream per block sizes are configurable and varies per usecase. + * L2 has constant block sizes - 2 blocks per stream. + * + * MMU1 support pre-fetching of the pages to have less cache lookup misses. To + * enable the pre-fetching, MMU1 AT (Address Translator) device registers + * need to be configured. + * + * There are four types of memory accesses which requires ZLW configuration. + * ZLW(Zero Length Write) is a mechanism to enable VT-d pre-fetching on IOMMU. + * + * 1. Sequential Access or 1D mode + * Set ZLW_EN -> 1 + * set ZLW_PAGE_CROSS_1D -> 1 + * Set ZLW_N to "N" pages so that ZLW will be inserte N pages ahead where + * N is pre-defined and hardcoded in the platform data + * Set ZLW_2D -> 0 + * + * 2. ZLW 2D mode + * Set ZLW_EN -> 1 + * set ZLW_PAGE_CROSS_1D -> 1, + * Set ZLW_N -> 0 + * Set ZLW_2D -> 1 + * + * 3. ZLW Enable (no 1D or 2D mode) + * Set ZLW_EN -> 1 + * set ZLW_PAGE_CROSS_1D -> 0, + * Set ZLW_N -> 0 + * Set ZLW_2D -> 0 + * + * 4. ZLW disable + * Set ZLW_EN -> 0 + * set ZLW_PAGE_CROSS_1D -> 0, + * Set ZLW_N -> 0 + * Set ZLW_2D -> 0 + * + * To configure the ZLW for the above memory access, four registers are + * available. Hence to track these four settings, we have the following entries + * in the struct ipu6_mmu_hw. Each of these entries are per stream and + * available only for the L1 streams. + * + * a. l1_zlw_en -> To track zlw enabled per stream (ZLW_EN) + * b. l1_zlw_1d_mode -> Track 1D mode per stream. ZLW inserted at page boundary + * c. l1_ins_zlw_ahead_pages -> to track how advance the ZLW need to be inserted + * Insert ZLW request N pages ahead address. + * d. l1_zlw_2d_mode -> To track 2D mode per stream (ZLW_2D) + * + * + * Currently L1/L2 streams, blocks, AT ZLW configurations etc. are pre-defined + * as per the usecase specific calculations. Any change to this pre-defined + * table has to happen in sync with IPU6 FW. + */ + +struct ipu6_mmu_hw { + union { + unsigned long offset; + void __iomem *base; + }; + u32 info_bits; + u8 nr_l1streams; + /* + * L1 has variable blocks per stream - total of 64 blocks and maximum of + * 16 blocks per stream. Configurable by using the block start address + * per stream. Block start address is calculated from the block size + */ + u8 l1_block_sz[IPU6_MMU_MAX_TLB_L1_STREAMS]; + /* Is ZLW is enabled in each stream */ + bool l1_zlw_en[IPU6_MMU_MAX_TLB_L1_STREAMS]; + bool l1_zlw_1d_mode[IPU6_MMU_MAX_TLB_L1_STREAMS]; + u8 l1_ins_zlw_ahead_pages[IPU6_MMU_MAX_TLB_L1_STREAMS]; + bool l1_zlw_2d_mode[IPU6_MMU_MAX_TLB_L1_STREAMS]; + + u32 l1_stream_id_reg_offset; + u32 l2_stream_id_reg_offset; + + u8 nr_l2streams; + /* + * L2 has fixed 2 blocks per stream. Block address is calculated + * from the block size + */ + u8 l2_block_sz[IPU6_MMU_MAX_TLB_L2_STREAMS]; + /* flag to track if WA is needed for successive invalidate HW bug */ + bool insert_read_before_invalidate; +}; + +struct ipu6_mmu_hw_ops { + int (*init_hw_data)(struct ipu6_mmu *mmu, struct device *dev, + void __iomem *base); + int (*hw_init)(struct ipu6_mmu *mmu); + void (*tlb_invalidate)(struct ipu6_mmu *mmu); +}; + struct ipu6_mmu { struct list_head node; - struct ipu6_mmu_hw *mmu_hw; + union { + struct ipu6_mmu_hw *ipu6_mmu_hw; + struct ipu7_mmu_hw *ipu7_mmu_hw; + }; unsigned int nr_mmus; unsigned int mmid; @@ -55,12 +193,14 @@ struct ipu6_mmu { bool ready; spinlock_t ready_lock; /* Serialize access to bool ready */ - void (*tlb_invalidate)(struct ipu6_mmu *mmu); + const struct ipu6_mmu_hw_ops *ops; }; +extern const struct ipu6_mmu_hw_ops ipu6_mmu_ops; +extern const struct ipu6_mmu_hw_ops ipu7_mmu_ops; + struct ipu6_mmu *ipu6_mmu_init(struct device *dev, - void __iomem *base, int mmid, - const struct ipu6_hw_variants *hw); + void __iomem *base, int mmid); void ipu6_mmu_cleanup(struct ipu6_mmu *mmu); int ipu6_mmu_hw_init(struct ipu6_mmu *mmu); void ipu6_mmu_hw_cleanup(struct ipu6_mmu *mmu); diff --git a/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h b/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h index efd65e494c16..57e661cc2177 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h +++ b/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h @@ -212,7 +212,7 @@ enum { #define BUTTRESS_TSW_CTL_SOFT_RESET BIT(8) #define BUTTRESS_REG_TSC_LO 0x164 -#define BUTTRESS_REG_TSC_HI 0x168 +#define BUTTRESS_TSC_HI_OFFSET 4 #define BUTTRESS_IRQS (BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING | \ BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE | \ @@ -221,4 +221,113 @@ enum { #define BUTTRESS_EVENT (BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING | \ BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE | \ BUTTRESS_ISR_SAI_VIOLATION) + +/* IPU7 */ + +/* IPU7 buttress register offsets */ +#define IPU7_BUTTRESS_REG_SKU 0x3030 +#define IPU7_BUTTRESS_REG_IRQ_STATUS 0x2000 +#define IPU7_BUTTRESS_REG_IRQ_ENABLE 0x2008 +#define IPU7_BUTTRESS_REG_IRQ_CLEAR 0x200c +#define IPU7_BUTTRESS_REG_IRQ_MASK 0x2010 +#define IPU7_BUTTRESS_REG_TSC_CMD 0x2014 +#define IPU7_BUTTRESS_REG_TSC_CTL 0x2018 +#define IPU7_BUTTRESS_REG_TSC_LO 0x201c +#define IPU7_BUTTRESS_REG_PB_TIMESTAMP_LO 0x2030 +#define IPU7_BUTTRESS_REG_PB_TIMESTAMP_VALID 0x2038 +#define IPU7_BUTTRESS_REG_IS_WORKPOINT_REQ 0x2104 +#define IPU7_BUTTRESS_REG_PS_WORKPOINT_REQ 0x2100 +#define IPU7_BUTTRESS_REG_IDLE_WDT 0x218c +#define IPU7_BUTTRESS_REG_ISYS_UCX_CTRL_STATUS 0x2200 +#define IPU7_BUTTRESS_REG_ISYS_UCX_START_ADDR 0x2204 +#define IPU7_BUTTRESS_REG_PSYS_UCX_CTRL_STATUS 0x2208 +#define IPU7_BUTTRESS_REG_PSYS_UCX_START_ADDR 0x220c +#define IPU7_BUTTRESS_REG_SECURITY_CTL 0x2318 +#define IPU7_BUTTRESS_REG_FW_RESET_CTL 0x2334 +#define IPU7_BUTTRESS_REG_FW_SOURCE_SIZE 0x2338 +#define IPU7_BUTTRESS_REG_FW_SOURCE_BASE 0x233c +#define IPU7_BUTTRESS_REG_CSE2IUDB0 0x2500 +#define IPU7_BUTTRESS_REG_CSE2IUDATA0 0x2504 +#define IPU7_BUTTRESS_REG_CSE2IUCSR 0x2508 +#define IPU7_BUTTRESS_REG_IU2CSEDB0 0x250c +#define IPU7_BUTTRESS_REG_IU2CSEDATA0 0x2510 +#define IPU7_BUTTRESS_REG_IU2CSECSR 0x2514 +#define IPU7_BUTTRESS_REG_CG_CTRL_BITS 0x3014 +#define IPU7_BUTTRESS_REG_FW_BOOT_PARAMS0 0x4000 +#define IPU7_BUTTRESS_REG_FW_BOOT_PARAMS7 0x401c +#define IPU7_BUTTRESS_REG_PWR_STATUS 0x2114 + +#define IPU7_BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE BIT(0) +#define IPU7_BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING BIT(1) +#define IPU7_BUTTRESS_IRQ_CSE_CSR_SET BIT(2) +#define IPU7_BUTTRESS_IRQ_SAI_VIOLATION BIT(4) +#define IPU7_BUTTRESS_IRQ_IS_IRQ BIT(30) +#define IPU7_BUTTRESS_IRQ_PS_IRQ BIT(31) + +#define IPU7_BUTTRESS_IRQS (IPU7_BUTTRESS_IRQ_IS_IRQ | \ + IPU7_BUTTRESS_IRQ_PS_IRQ | \ + IPU7_BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING | \ + IPU7_BUTTRESS_IRQ_CSE_CSR_SET | \ + IPU7_BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE) + +/* LNL SW workaround for PS PD hang */ +#define IPU7_BUTTRESS_CG_CTRL_PS_FSM_CG BIT(3) + +/* P-unit Bridge (PB) BAR registers */ +#define IPU7_PB_INTERRUPT_STATUS 0x0 + +/* Frequency control encoding */ +#define IPU7_FREQ_CTL_CDYN 0x80 +#define IPU7_FREQ_CTL_CDYN_SHIFT 8 +#define IPU7_BUTTRESS_IS_FREQ_CTL_RATIO_MASK GENMASK(7, 0) +#define IPU7_IS_FREQ_CTL_DEFAULT_RATIO 0x1b +#define IPU7_PS_FREQ_CTL_DEFAULT_RATIO 0x14 + +/* D2D power control */ +#define IPU7_BUTTRESS_REG_D2D_CTL 0x21d4 +#define IPU7_BUTTRESS_D2D_PWR_EN BIT(0) +#define IPU7_BUTTRESS_D2D_PWR_ACK BIT(4) + +#define IPU7_BUTTRESS_PWR_STATE_IS_PWR_SHIFT 0 +#define IPU7_BUTTRESS_PWR_STATE_IS_PWR_MASK (0x3U << 0) +#define IPU7_BUTTRESS_PWR_STATE_PS_PWR_SHIFT 4 +#define IPU7_BUTTRESS_PWR_STATE_PS_PWR_MASK (0x3U << 4) + +/* NDE */ +#define IPU7_BUTTRESS_REG_NDE_CONTROL 0x21a4 +#define IPU7_NDE_VAL_MASK GENMASK(9, 0) +#define IPU7_NDE_SCALE_MASK GENMASK(12, 10) +#define IPU7_NDE_VALID_MASK BIT(13) +#define IPU7_NDE_RESVEC_MASK GENMASK(19, 16) +#define IPU7_NDE_VAL_ACTIVE 48 +#define IPU7_NDE_SCALE_ACTIVE 2 +#define IPU7_NDE_VALID_ACTIVE 1 +#define IPU7_NDE_VAL_DEFAULT 1023 +#define IPU7_NDE_SCALE_DEFAULT 2 +#define IPU7_NDE_VALID_DEFAULT 0 +#define IPU7_NDE_RESVEC 0xe + +/* IS UCX control */ +#define IPU7_UCX_CTL_RESET BIT(0) +#define IPU7_UCX_CTL_RUN BIT(1) +#define IPU7_UCX_CTL_WAKEUP BIT(2) + +#define IPU7_BUTTRESS_REG_SLEEP_LEVEL_CFG 0x21b0 +#define IPU7_BUTTRESS_REG_SLEEP_LEVEL_STS 0x21b4 +#define IPU7_BUTTRESS_OVERRIDE_IS_CLK BIT(1) +#define IPU7_BUTTRESS_OWN_ACK_IS_CLK BIT(9) +#define IPU7_BUTTRESS_OVERRIDE_PS_CLK BIT(2) +#define IPU7_BUTTRESS_OWN_ACK_PS_CLK BIT(10) + +/* PB registers */ +#define IPU7_GLOBAL_INTERRUPT_MASK 0x8 +#define IPU7_TLBID_HASH_ENABLE_31_0 0x30 +#define IPU7_TLBID_HASH_ENABLE_63_32 0x34 +#define IPU7_TLBID_HASH_ENABLE_95_64 0x38 +#define IPU7_TLBID_HASH_ENABLE_127_96 0x3c +#define IPU7_BAR2_MISC_CONFIG 0x64 + +/* IPU7.5 */ +#define IPU7_BUTTRESS_SEL_PB_TIMESTAMP BIT(9) + #endif /* IPU6_PLATFORM_BUTTRESS_REGS_H */ diff --git a/drivers/media/pci/intel/ipu6/ipu6.c b/drivers/media/pci/intel/ipu6/ipu6.c index 5449a2006bcc..43d951735f72 100644 --- a/drivers/media/pci/intel/ipu6/ipu6.c +++ b/drivers/media/pci/intel/ipu6/ipu6.c @@ -19,6 +19,7 @@ #include <linux/scatterlist.h> #include <linux/slab.h> #include <linux/types.h> +#include <linux/vmalloc.h> #include <media/ipu-bridge.h> #include <media/ipu6-pci-table.h> @@ -27,13 +28,20 @@ #include "ipu6-bus.h" #include "ipu6-buttress.h" #include "ipu6-cpd.h" +#include "ipu6-dma.h" #include "ipu6-isys.h" #include "ipu6-mmu.h" #include "ipu6-platform-buttress-regs.h" #include "ipu6-platform-isys-csi2-reg.h" #include "ipu6-platform-regs.h" +#include "ipu7-isys-csi2-regs.h" #define IPU6_PCI_BAR 0 +#define IPU7_PCI_PBBAR 4 + +static int force_no_probe_ipu7 = !IS_BUILTIN(CONFIG_VIDEO_INTEL_IPU6_IPU7); +module_param(force_no_probe_ipu7, int, 0644); +MODULE_PARM_DESC(force_no_probe_ipu7, "Don't probe ipu7 and ipu7.5 devices"); struct ipu6_cell_program { u32 magic_number; @@ -73,54 +81,6 @@ struct ipu6_cell_program { static struct ipu6_isys_internal_pdata isys_ipdata = { .hw_variant = { .offset = IPU6_UNIFIED_OFFSET, - .nr_mmus = 3, - .mmu_hw = { - { - .offset = IPU6_ISYS_IOMMU0_OFFSET, - .info_bits = IPU6_INFO_REQUEST_DESTINATION_IOSF, - .nr_l1streams = 16, - .l1_block_sz = { - 3, 8, 2, 2, 2, 2, 2, 2, 1, 1, - 1, 1, 1, 1, 1, 1 - }, - .nr_l2streams = 16, - .l2_block_sz = { - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2 - }, - .insert_read_before_invalidate = false, - .l1_stream_id_reg_offset = - IPU6_MMU_L1_STREAM_ID_REG_OFFSET, - .l2_stream_id_reg_offset = - IPU6_MMU_L2_STREAM_ID_REG_OFFSET, - }, - { - .offset = IPU6_ISYS_IOMMU1_OFFSET, - .info_bits = 0, - .nr_l1streams = 16, - .l1_block_sz = { - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 1, 1, 4 - }, - .nr_l2streams = 16, - .l2_block_sz = { - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2 - }, - .insert_read_before_invalidate = false, - .l1_stream_id_reg_offset = - IPU6_MMU_L1_STREAM_ID_REG_OFFSET, - .l2_stream_id_reg_offset = - IPU6_MMU_L2_STREAM_ID_REG_OFFSET, - }, - { - .offset = IPU6_ISYS_IOMMUI_OFFSET, - .info_bits = 0, - .nr_l1streams = 0, - .nr_l2streams = 0, - .insert_read_before_invalidate = false, - }, - }, .cdc_fifos = 3, .cdc_fifo_threshold = {6, 8, 2}, .dmem_offset = IPU6_ISYS_DMEM_OFFSET, @@ -132,83 +92,12 @@ static struct ipu6_isys_internal_pdata isys_ipdata = { static struct ipu6_psys_internal_pdata psys_ipdata = { .hw_variant = { .offset = IPU6_UNIFIED_OFFSET, - .nr_mmus = 4, - .mmu_hw = { - { - .offset = IPU6_PSYS_IOMMU0_OFFSET, - .info_bits = - IPU6_INFO_REQUEST_DESTINATION_IOSF, - .nr_l1streams = 16, - .l1_block_sz = { - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2 - }, - .nr_l2streams = 16, - .l2_block_sz = { - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2 - }, - .insert_read_before_invalidate = false, - .l1_stream_id_reg_offset = - IPU6_MMU_L1_STREAM_ID_REG_OFFSET, - .l2_stream_id_reg_offset = - IPU6_MMU_L2_STREAM_ID_REG_OFFSET, - }, - { - .offset = IPU6_PSYS_IOMMU1_OFFSET, - .info_bits = 0, - .nr_l1streams = 32, - .l1_block_sz = { - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 10, - 5, 4, 14, 6, 4, 14, 6, 4, 8, - 4, 2, 1, 1, 1, 1, 14 - }, - .nr_l2streams = 32, - .l2_block_sz = { - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2 - }, - .insert_read_before_invalidate = false, - .l1_stream_id_reg_offset = - IPU6_MMU_L1_STREAM_ID_REG_OFFSET, - .l2_stream_id_reg_offset = - IPU6_PSYS_MMU1W_L2_STREAM_ID_REG_OFFSET, - }, - { - .offset = IPU6_PSYS_IOMMU1R_OFFSET, - .info_bits = 0, - .nr_l1streams = 16, - .l1_block_sz = { - 1, 4, 4, 4, 4, 16, 8, 4, 32, - 16, 16, 2, 2, 2, 1, 12 - }, - .nr_l2streams = 16, - .l2_block_sz = { - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2 - }, - .insert_read_before_invalidate = false, - .l1_stream_id_reg_offset = - IPU6_MMU_L1_STREAM_ID_REG_OFFSET, - .l2_stream_id_reg_offset = - IPU6_MMU_L2_STREAM_ID_REG_OFFSET, - }, - { - .offset = IPU6_PSYS_IOMMUI_OFFSET, - .info_bits = 0, - .nr_l1streams = 0, - .nr_l2streams = 0, - .insert_read_before_invalidate = false, - }, - }, .dmem_offset = IPU6_PSYS_DMEM_OFFSET, }, }; -static const struct ipu6_buttress_ctrl isys_buttress_ctrl = { +static const struct ipu6_buttress_ctrl ipu6_isys_buttress_ctrl = { + .subsys_id = IPU_ISYS, .ratio = IPU6_IS_FREQ_CTL_DEFAULT_RATIO, .qos_floor = IPU6_IS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO, .freq_ctl = IPU6_BUTTRESS_REG_IS_FREQ_CTL, @@ -218,7 +107,8 @@ static const struct ipu6_buttress_ctrl isys_buttress_ctrl = { .pwr_sts_off = IPU6_BUTTRESS_PWR_STATE_DN_DONE, }; -static const struct ipu6_buttress_ctrl psys_buttress_ctrl = { +static const struct ipu6_buttress_ctrl ipu6_psys_buttress_ctrl = { + .subsys_id = IPU_PSYS, .ratio = IPU6_PS_FREQ_CTL_DEFAULT_RATIO, .qos_floor = IPU6_PS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO, .freq_ctl = IPU6_BUTTRESS_REG_PS_FREQ_CTL, @@ -228,6 +118,119 @@ static const struct ipu6_buttress_ctrl psys_buttress_ctrl = { .pwr_sts_off = IPU6_BUTTRESS_PWR_STATE_DN_DONE, }; +static const struct ipu6_buttress_ctrl ipu7_isys_buttress_ctrl = { + .subsys_id = IPU_ISYS, + .ratio = IPU7_IS_FREQ_CTL_DEFAULT_RATIO, + .qos_floor = 0, + .freq_ctl = IPU7_BUTTRESS_REG_IS_WORKPOINT_REQ, + .pwr_sts_shift = IPU7_BUTTRESS_PWR_STATE_IS_PWR_SHIFT, + .pwr_sts_mask = IPU7_BUTTRESS_PWR_STATE_IS_PWR_MASK, + .pwr_sts_on = IPU6_BUTTRESS_PWR_STATE_UP_DONE, + .pwr_sts_off = IPU6_BUTTRESS_PWR_STATE_DN_DONE, +}; + +static const struct ipu6_buttress_ctrl ipu7_psys_buttress_ctrl = { + .subsys_id = IPU_PSYS, + .ratio = IPU7_PS_FREQ_CTL_DEFAULT_RATIO, + .qos_floor = 0, + .freq_ctl = IPU7_BUTTRESS_REG_PS_WORKPOINT_REQ, + .pwr_sts_shift = IPU7_BUTTRESS_PWR_STATE_PS_PWR_SHIFT, + .pwr_sts_mask = IPU7_BUTTRESS_PWR_STATE_PS_PWR_MASK, + .pwr_sts_on = IPU6_BUTTRESS_PWR_STATE_UP_DONE, + .pwr_sts_off = IPU6_BUTTRESS_PWR_STATE_DN_DONE, +}; + +static const struct ipu6_buttress_registers ipu6_buttress_regs = { + /* Registers */ + .irq_status = BUTTRESS_REG_ISR_STATUS, + .irq_clear = BUTTRESS_REG_ISR_CLEAR, + .irq_enable = BUTTRESS_REG_ISR_ENABLE, + .pwr_status = BUTTRESS_REG_PWR_STATE, + .security_ctl = BUTTRESS_REG_SECURITY_CTL, + .fw_reset_ctl = BUTTRESS_REG_FW_RESET_CTL, + .fabric_cmd = BUTTRESS_REG_FABRIC_CMD, + .tsw_ctl = BUTTRESS_REG_TSW_CTL, + .tsc_lo = BUTTRESS_REG_TSC_LO, + .wdt = BUTTRESS_REG_WDT, + .btrs_ctrl = BUTTRESS_REG_BTRS_CTRL, + .csr_in = BUTTRESS_REG_CSE2IUCSR, + .csr_out = BUTTRESS_REG_IU2CSECSR, + .db0_in = BUTTRESS_REG_CSE2IUDB0, + .db0_out = BUTTRESS_REG_IU2CSEDB0, + .data0_in = BUTTRESS_REG_CSE2IUDATA0, + .data0_out = BUTTRESS_REG_IU2CSEDATA0, + .sku_id = BUTTRESS_REG_SKU, + + /* Bitmasks */ + .irq_is = BUTTRESS_ISR_IS_IRQ, + .irq_ps = BUTTRESS_ISR_PS_IRQ, + .irq_all = BUTTRESS_IRQS, + .irq_events = BUTTRESS_EVENT, + .irq_cse_ipc = BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING, + .irq_exec_done = BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE, + .irq_sai = BUTTRESS_ISR_SAI_VIOLATION, +}; + +static const struct ipu6_buttress_registers ipu7_buttress_regs = { + /* Registers */ + .irq_status = IPU7_BUTTRESS_REG_IRQ_STATUS, + .irq_clear = IPU7_BUTTRESS_REG_IRQ_CLEAR, + .irq_enable = IPU7_BUTTRESS_REG_IRQ_ENABLE, + .pwr_status = IPU7_BUTTRESS_REG_PWR_STATUS, + .security_ctl = IPU7_BUTTRESS_REG_SECURITY_CTL, + .fw_reset_ctl = IPU7_BUTTRESS_REG_FW_RESET_CTL, + .fabric_cmd = IPU7_BUTTRESS_REG_TSC_CMD, + .tsw_ctl = IPU7_BUTTRESS_REG_TSC_CTL, + .tsc_lo = IPU7_BUTTRESS_REG_TSC_LO, + .wdt = IPU7_BUTTRESS_REG_IDLE_WDT, + .csr_in = IPU7_BUTTRESS_REG_CSE2IUCSR, + .csr_out = IPU7_BUTTRESS_REG_IU2CSECSR, + .db0_in = IPU7_BUTTRESS_REG_CSE2IUDB0, + .db0_out = IPU7_BUTTRESS_REG_IU2CSEDB0, + .data0_in = IPU7_BUTTRESS_REG_CSE2IUDATA0, + .data0_out = IPU7_BUTTRESS_REG_IU2CSEDATA0, + .sku_id = IPU7_BUTTRESS_REG_SKU, + + /* Bitmasks */ + .irq_is = IPU7_BUTTRESS_IRQ_IS_IRQ, + .irq_ps = IPU7_BUTTRESS_IRQ_PS_IRQ, + .irq_all = IPU7_BUTTRESS_IRQS, + .irq_events = IPU7_BUTTRESS_IRQS, + .irq_cse_ipc = IPU7_BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING, + .irq_exec_done = IPU7_BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE, + .irq_sai = IPU7_BUTTRESS_IRQ_SAI_VIOLATION, +}; + +static const struct ipu6_buttress_registers ipu7p5_buttress_regs = { + /* Registers */ + .irq_status = IPU7_BUTTRESS_REG_IRQ_STATUS, + .irq_clear = IPU7_BUTTRESS_REG_IRQ_CLEAR, + .irq_enable = IPU7_BUTTRESS_REG_IRQ_ENABLE, + .pwr_status = IPU7_BUTTRESS_REG_PWR_STATUS, + .security_ctl = IPU7_BUTTRESS_REG_SECURITY_CTL, + .fw_reset_ctl = IPU7_BUTTRESS_REG_FW_RESET_CTL, + .fabric_cmd = IPU7_BUTTRESS_REG_TSC_CMD, + .tsw_ctl = IPU7_BUTTRESS_REG_TSC_CTL, + .tsc_lo = IPU7_BUTTRESS_REG_PB_TIMESTAMP_LO, + .wdt = IPU7_BUTTRESS_REG_IDLE_WDT, + .csr_in = IPU7_BUTTRESS_REG_CSE2IUCSR, + .csr_out = IPU7_BUTTRESS_REG_IU2CSECSR, + .db0_in = IPU7_BUTTRESS_REG_CSE2IUDB0, + .db0_out = IPU7_BUTTRESS_REG_IU2CSEDB0, + .data0_in = IPU7_BUTTRESS_REG_CSE2IUDATA0, + .data0_out = IPU7_BUTTRESS_REG_IU2CSEDATA0, + .sku_id = IPU7_BUTTRESS_REG_SKU, + + /* Bitmasks */ + .irq_is = IPU7_BUTTRESS_IRQ_IS_IRQ, + .irq_ps = IPU7_BUTTRESS_IRQ_PS_IRQ, + .irq_all = IPU7_BUTTRESS_IRQS, + .irq_events = IPU7_BUTTRESS_IRQS, + .irq_cse_ipc = IPU7_BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING, + .irq_exec_done = IPU7_BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE, + .irq_sai = IPU7_BUTTRESS_IRQ_SAI_VIOLATION, +}; + static void ipu6_pkg_dir_configure_spc(struct ipu6_device *isp, const struct ipu6_hw_variants *hw_variant, @@ -267,10 +270,16 @@ void ipu6_configure_spc(struct ipu6_device *isp, int pkg_dir_idx, void __iomem *base, u64 *pkg_dir, dma_addr_t pkg_dir_dma_addr) { - void __iomem *dmem_base = base + hw_variant->dmem_offset; - void __iomem *spc_regs_base = base + hw_variant->spc_offset; + void __iomem *dmem_base; + void __iomem *spc_regs_base; u32 val; + if (IS_IPU7(isp)) + return; + + dmem_base = base + hw_variant->dmem_offset; + spc_regs_base = base + hw_variant->spc_offset; + val = readl(spc_regs_base + IPU6_PSYS_REG_SPC_STATUS_CTRL); val |= IPU6_PSYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE; writel(val, spc_regs_base + IPU6_PSYS_REG_SPC_STATUS_CTRL); @@ -290,8 +299,6 @@ EXPORT_SYMBOL_NS_GPL(ipu6_configure_spc, "INTEL_IPU6"); static void ipu6_internal_pdata_init(struct ipu6_device *isp) { - u8 hw_ver = isp->hw_ver; - isys_ipdata.num_parallel_streams = IPU6_ISYS_NUM_STREAMS; isys_ipdata.sram_gran_shift = IPU6_SRAM_GRANULARITY_SHIFT; isys_ipdata.sram_gran_size = IPU6_SRAM_GRANULARITY_SIZE; @@ -314,19 +321,19 @@ static void ipu6_internal_pdata_init(struct ipu6_device *isp) IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS; isys_ipdata.csi2.ctrl0_irq_lnp = IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE; - isys_ipdata.enhanced_iwake = is_ipu6ep_mtl(hw_ver) || is_ipu6ep(hw_ver); + isys_ipdata.enhanced_iwake = IS_IPU6EP_MTL(isp) || IS_IPU6EP(isp); psys_ipdata.hw_variant.spc_offset = IPU6_PSYS_SPC_OFFSET; isys_ipdata.csi2.fw_access_port_ofs = CSI_REG_HUB_FW_ACCESS_PORT_OFS; - if (is_ipu6ep(hw_ver)) { + if (IS_IPU6EP(isp)) { isys_ipdata.ltr = IPU6EP_LTR_VALUE; isys_ipdata.memopen_threshold = IPU6EP_MIN_MEMOPEN_TH; } - if (is_ipu6_tgl(hw_ver)) + if (IS_IPU6_TGL(isp)) isys_ipdata.csi2.nports = IPU6_TGL_ISYS_CSI2_NPORTS; - if (is_ipu6ep_mtl(hw_ver)) { + if (IS_IPU6EP_MTL(isp)) { isys_ipdata.csi2.nports = IPU6EP_MTL_ISYS_CSI2_NPORTS; isys_ipdata.csi2.ctrl0_irq_edge = @@ -347,7 +354,7 @@ static void ipu6_internal_pdata_init(struct ipu6_device *isp) isys_ipdata.memopen_threshold = IPU6EP_MTL_MIN_MEMOPEN_TH; } - if (is_ipu6se(hw_ver)) { + if (IS_IPU6SE(isp)) { isys_ipdata.csi2.nports = IPU6SE_ISYS_CSI2_NPORTS; isys_ipdata.csi2.irq_mask = IPU6SE_CSI_RX_ERROR_IRQ_MASK; isys_ipdata.num_parallel_streams = IPU6SE_ISYS_NUM_STREAMS; @@ -363,15 +370,21 @@ static void ipu6_internal_pdata_init(struct ipu6_device *isp) isys_ipdata.max_devq_size = IPU6SE_DEV_SEND_QUEUE_SIZE; psys_ipdata.hw_variant.spc_offset = IPU6SE_PSYS_SPC_OFFSET; } + + if (IS_IPU7(isp)) { + isys_ipdata.csi2.gpreg = IPU7_IS_IO_CSI2_GPREGS_BASE; + isys_ipdata.csi2.nports = 4; + } } static struct ipu6_bus_device * ipu6_isys_init(struct pci_dev *pdev, struct device *parent, - struct ipu6_buttress_ctrl *ctrl, void __iomem *base, + const struct ipu6_buttress_ctrl *ctrl, void __iomem *base, const struct ipu6_isys_internal_pdata *ipdata) { struct device *dev = &pdev->dev; struct ipu6_bus_device *isys_adev; + struct ipu6_buttress_ctrl *devm_ctrl; struct ipu6_isys_pdata *pdata; int ret; @@ -381,6 +394,10 @@ ipu6_isys_init(struct pci_dev *pdev, struct device *parent, return ERR_PTR(ret); } + devm_ctrl = devm_kmemdup(dev, ctrl, sizeof(*ctrl), GFP_KERNEL); + if (!devm_ctrl) + return ERR_PTR(-ENOMEM); + pdata = kzalloc_obj(*pdata); if (!pdata) return ERR_PTR(-ENOMEM); @@ -388,7 +405,7 @@ ipu6_isys_init(struct pci_dev *pdev, struct device *parent, pdata->base = base; pdata->ipdata = ipdata; - isys_adev = ipu6_bus_initialize_device(pdev, parent, pdata, ctrl, + isys_adev = ipu6_bus_initialize_device(pdev, parent, pdata, devm_ctrl, IPU6_ISYS_NAME); if (IS_ERR(isys_adev)) { kfree(pdata); @@ -396,8 +413,7 @@ ipu6_isys_init(struct pci_dev *pdev, struct device *parent, "ipu6_bus_initialize_device isys failed\n"); } - isys_adev->mmu = ipu6_mmu_init(dev, base, ISYS_MMID, - &ipdata->hw_variant); + isys_adev->mmu = ipu6_mmu_init(dev, base, IPU_ISYS); if (IS_ERR(isys_adev->mmu)) { put_device(&isys_adev->auxdev.dev); return dev_err_cast_probe(dev, isys_adev->mmu, @@ -415,13 +431,19 @@ ipu6_isys_init(struct pci_dev *pdev, struct device *parent, static struct ipu6_bus_device * ipu6_psys_init(struct pci_dev *pdev, struct device *parent, - struct ipu6_buttress_ctrl *ctrl, void __iomem *base, + const struct ipu6_buttress_ctrl *ctrl, void __iomem *base, const struct ipu6_psys_internal_pdata *ipdata) { + struct device *dev = &pdev->dev; struct ipu6_bus_device *psys_adev; + struct ipu6_buttress_ctrl *devm_ctrl; struct ipu6_psys_pdata *pdata; int ret; + devm_ctrl = devm_kmemdup(dev, ctrl, sizeof(*ctrl), GFP_KERNEL); + if (!devm_ctrl) + return ERR_PTR(-ENOMEM); + pdata = kzalloc_obj(*pdata); if (!pdata) return ERR_PTR(-ENOMEM); @@ -429,7 +451,7 @@ ipu6_psys_init(struct pci_dev *pdev, struct device *parent, pdata->base = base; pdata->ipdata = ipdata; - psys_adev = ipu6_bus_initialize_device(pdev, parent, pdata, ctrl, + psys_adev = ipu6_bus_initialize_device(pdev, parent, pdata, devm_ctrl, IPU6_PSYS_NAME); if (IS_ERR(psys_adev)) { kfree(pdata); @@ -437,8 +459,7 @@ ipu6_psys_init(struct pci_dev *pdev, struct device *parent, "ipu6_bus_initialize_device psys failed\n"); } - psys_adev->mmu = ipu6_mmu_init(&pdev->dev, base, PSYS_MMID, - &ipdata->hw_variant); + psys_adev->mmu = ipu6_mmu_init(&pdev->dev, base, IPU_PSYS); if (IS_ERR(psys_adev->mmu)) { put_device(&psys_adev->auxdev.dev); return dev_err_cast_probe(&pdev->dev, psys_adev->mmu, @@ -454,12 +475,13 @@ ipu6_psys_init(struct pci_dev *pdev, struct device *parent, return psys_adev; } -static int ipu6_pci_config_setup(struct pci_dev *dev, u8 hw_ver) +static int ipu6_pci_config_setup(struct pci_dev *dev) { + struct ipu6_device *isp = pci_get_drvdata(dev); int ret; /* No PCI msi capability for IPU6EP */ - if (is_ipu6ep(hw_ver) || is_ipu6ep_mtl(hw_ver)) { + if (IS_IPU6EP(isp) || IS_IPU6EP_MTL(isp)) { /* likely do nothing as msi not enabled by default */ pci_disable_msi(dev); return 0; @@ -474,7 +496,12 @@ static int ipu6_pci_config_setup(struct pci_dev *dev, u8 hw_ver) static void ipu6_configure_vc_mechanism(struct ipu6_device *isp) { - u32 val = readl(isp->base + BUTTRESS_REG_BTRS_CTRL); + u32 val; + + if (IS_IPU7(isp)) + return; + + val = readl(isp->base + BUTTRESS_REG_BTRS_CTRL); if (IPU6_BTRS_ARB_STALL_MODE_VC0 == IPU6_BTRS_ARB_MODE_TYPE_STALL) val |= BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0; @@ -489,70 +516,193 @@ static void ipu6_configure_vc_mechanism(struct ipu6_device *isp) writel(val, isp->base + BUTTRESS_REG_BTRS_CTRL); } +static int __ipu6_map_fw_by_sys(struct ipu6_device *isp, struct ipu6_bus_device *adev) +{ + int ret; + + ret = ipu6_map_fw_region(adev, isp->cpd_fw->data, isp->cpd_fw->size, + DMA_TO_DEVICE, 0); + if (ret) { + dev_err_probe(&isp->pdev->dev, ret, + "Firmware mapping failed\n"); + return ret; + } + + ret = ipu6_cpd_create_pkg_dir(adev, isp->cpd_fw->data); + if (ret) { + dev_err_probe(&isp->pdev->dev, ret, + "failed to create pkg dir\n"); + return ret; + } + + return 0; +} + +static int ipu6_map_fw(struct ipu6_device *isp) +{ + int ret; + + ret = __ipu6_map_fw_by_sys(isp, isp->psys); + if (ret) + return ret; + + if (!isp->secure_mode) + return __ipu6_map_fw_by_sys(isp, isp->isys); + + return 0; +} + +static int __ipu7_map_fw_non_secure(struct ipu6_device *isp) +{ + int ret; + + /* + * Allocate and map memory for running the firmware. Not + * required in secure mode, in which firmware runs in IMR. + */ + isp->fw_code_region = vmalloc(IPU7_FW_CODE_REGION_SIZE); + if (!isp->fw_code_region) + return -ENOMEM; + + ret = ipu6_ipu7_cpd_copy_binary(isp->cpd_fw->data, "isys", + isp->fw_code_region, + &isp->isys->fw_entry); + if (ret) + return ret; + + ret = ipu6_map_fw_region(isp->isys, isp->fw_code_region, + IPU7_FW_CODE_REGION_SIZE, DMA_BIDIRECTIONAL, + DMA_ATTR_RESERVE_REGION); + if (ret) + return ret; + + ret = ipu6_ipu7_cpd_copy_binary(isp->cpd_fw->data, "psys", + isp->fw_code_region, + &isp->psys->fw_entry); + if (ret) + return ret; + + return ipu6_map_fw_region(isp->psys, isp->fw_code_region, + IPU7_FW_CODE_REGION_SIZE, DMA_BIDIRECTIONAL, + DMA_ATTR_RESERVE_REGION); +} + +static int ipu7_map_fw(struct ipu6_device *isp) +{ + int ret; + + ret = isp->secure_mode ? + ipu6_map_fw_region(isp->psys, isp->cpd_fw->data, + isp->cpd_fw->size, DMA_BIDIRECTIONAL, 0) : + __ipu7_map_fw_non_secure(isp); + if (ret) { + dev_err_probe(&isp->pdev->dev, ret, + "Failed to init ipu7 firmware region\n"); + return ret; + } + + return 0; +} + static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - struct ipu6_buttress_ctrl *isys_ctrl = NULL, *psys_ctrl = NULL; + const struct ipu6_buttress_ctrl *isys_ctrl, *psys_ctrl; struct device *dev = &pdev->dev; void __iomem *isys_base = NULL; void __iomem *psys_base = NULL; struct ipu6_device *isp; phys_addr_t phys; u32 val, version, sku_id; + unsigned long dir; int ret; + if ((id->device == PCI_DEVICE_ID_INTEL_IPU7 || + id->device == PCI_DEVICE_ID_INTEL_IPU7P5) && force_no_probe_ipu7) + return -ENODEV; + isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL); if (!isp) return -ENOMEM; - isp->pdev = pdev; - INIT_LIST_HEAD(&isp->devices); - - ret = pcim_enable_device(pdev); - if (ret) - return dev_err_probe(dev, ret, "Enable PCI device failed\n"); - - phys = pci_resource_start(pdev, IPU6_PCI_BAR); - dev_dbg(dev, "IPU6 PCI bar[%u] = %pa\n", IPU6_PCI_BAR, &phys); - - isp->base = pcim_iomap_region(pdev, IPU6_PCI_BAR, IPU6_NAME); - if (IS_ERR(isp->base)) - return dev_err_probe(dev, PTR_ERR(isp->base), - "Failed to I/O mem remapping\n"); - - pci_set_drvdata(pdev, isp); - pci_set_master(pdev); - isp->cpd_metadata_cmpnt_size = sizeof(struct ipu6_cpd_metadata_cmpnt); + isp->buttress.regs = &ipu6_buttress_regs; + isp->model_name = IPU6_MEDIA_DEV_MODEL_NAME; + isys_ctrl = &ipu6_isys_buttress_ctrl; + psys_ctrl = &ipu6_psys_buttress_ctrl; + switch (id->device) { case PCI_DEVICE_ID_INTEL_IPU6: - isp->hw_ver = IPU6_VER_6; + isp->hw_ver = IPU_VERSION_6; isp->cpd_fw_name = IPU6_FIRMWARE_NAME; break; case PCI_DEVICE_ID_INTEL_IPU6SE: - isp->hw_ver = IPU6_VER_6SE; + isp->hw_ver = IPU_VERSION_6SE; isp->cpd_fw_name = IPU6SE_FIRMWARE_NAME; isp->cpd_metadata_cmpnt_size = sizeof(struct ipu6se_cpd_metadata_cmpnt); break; case PCI_DEVICE_ID_INTEL_IPU6EP_ADLP: case PCI_DEVICE_ID_INTEL_IPU6EP_RPLP: - isp->hw_ver = IPU6_VER_6EP; + isp->hw_ver = IPU_VERSION_6EP; isp->cpd_fw_name = IPU6EP_FIRMWARE_NAME; break; case PCI_DEVICE_ID_INTEL_IPU6EP_ADLN: - isp->hw_ver = IPU6_VER_6EP; + isp->hw_ver = IPU_VERSION_6EP; isp->cpd_fw_name = IPU6EPADLN_FIRMWARE_NAME; break; case PCI_DEVICE_ID_INTEL_IPU6EP_MTL: - isp->hw_ver = IPU6_VER_6EP_MTL; + isp->hw_ver = IPU_VERSION_6EP_MTL; isp->cpd_fw_name = IPU6EPMTL_FIRMWARE_NAME; break; + case PCI_DEVICE_ID_INTEL_IPU7: + isp->hw_ver = IPU_VERSION_7; + isp->cpd_fw_name = IPU7_FIRMWARE_NAME; + isp->model_name = IPU7_MEDIA_DEV_MODEL_NAME; + isp->buttress.regs = &ipu7_buttress_regs; + isys_ctrl = &ipu7_isys_buttress_ctrl; + psys_ctrl = &ipu7_psys_buttress_ctrl; + break; + case PCI_DEVICE_ID_INTEL_IPU7P5: + isp->hw_ver = IPU_VERSION_7P5; + isp->cpd_fw_name = IPU7P5_FIRMWARE_NAME; + isp->model_name = IPU7P5_MEDIA_DEV_MODEL_NAME; + isp->buttress.regs = &ipu7p5_buttress_regs; + isys_ctrl = &ipu7_isys_buttress_ctrl; + psys_ctrl = &ipu7_psys_buttress_ctrl; + break; default: return dev_err_probe(dev, -ENODEV, "Unsupported IPU6 device %x\n", id->device); } + isp->pdev = pdev; + INIT_LIST_HEAD(&isp->devices); + + ret = pcim_enable_device(pdev); + if (ret) + return dev_err_probe(dev, ret, "Enable PCI device failed\n"); + + phys = pci_resource_start(pdev, IPU6_PCI_BAR); + dev_dbg(dev, "IPU6 PCI bar[%u] = %pa\n", IPU6_PCI_BAR, &phys); + + isp->base = pcim_iomap_region(pdev, IPU6_PCI_BAR, IPU6_NAME); + if (IS_ERR(isp->base)) + return dev_err_probe(dev, PTR_ERR(isp->base), + "Failed to I/O mem remapping\n"); + + if (IS_IPU7(isp)) { + isp->pb_base = pcim_iomap_region(pdev, IPU7_PCI_PBBAR, + IPU6_NAME); + if (IS_ERR(isp->pb_base)) + return dev_err_probe(dev, PTR_ERR(isp->pb_base), + "I/O remapping PB BAR %u failed\n", + IPU7_PCI_PBBAR); + } + + pci_set_drvdata(pdev, isp); + pci_set_master(pdev); + ipu6_internal_pdata_init(isp); isys_base = isp->base + isys_ipdata.hw_variant.offset; @@ -564,7 +714,7 @@ static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) dma_set_max_seg_size(dev, UINT_MAX); - ret = ipu6_pci_config_setup(pdev, isp->hw_ver); + ret = ipu6_pci_config_setup(pdev); if (ret) return ret; @@ -588,13 +738,6 @@ static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto out_ipu6_bus_del_devices; } - isys_ctrl = devm_kmemdup(dev, &isys_buttress_ctrl, - sizeof(isys_buttress_ctrl), GFP_KERNEL); - if (!isys_ctrl) { - ret = -ENOMEM; - goto out_ipu6_bus_del_devices; - } - isp->isys = ipu6_isys_init(pdev, dev, isys_ctrl, isys_base, &isys_ipdata); if (IS_ERR(isp->isys)) { @@ -602,13 +745,6 @@ static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto out_ipu6_bus_del_devices; } - psys_ctrl = devm_kmemdup(dev, &psys_buttress_ctrl, - sizeof(psys_buttress_ctrl), GFP_KERNEL); - if (!psys_ctrl) { - ret = -ENOMEM; - goto out_ipu6_bus_del_devices; - } - isp->psys = ipu6_psys_init(pdev, &isp->isys->auxdev.dev, psys_ctrl, psys_base, &psys_ipdata); if (IS_ERR(isp->psys)) { @@ -627,19 +763,9 @@ static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto out_ipu6_rpm_put; } - ret = ipu6_buttress_map_fw_image(isp->psys, isp->cpd_fw, - &isp->psys->fw_sgt); - if (ret) { - dev_err_probe(&isp->pdev->dev, ret, "failed to map fw image\n"); - goto out_ipu6_rpm_put; - } - - ret = ipu6_cpd_create_pkg_dir(isp->psys, isp->cpd_fw->data); - if (ret) { - dev_err_probe(&isp->pdev->dev, ret, - "failed to create pkg dir\n"); + ret = IS_IPU7(isp) ? ipu7_map_fw(isp) : ipu6_map_fw(isp); + if (ret) goto out_ipu6_rpm_put; - } ret = devm_request_threaded_irq(dev, pdev->irq, ipu6_buttress_isr, ipu6_buttress_isr_threaded, @@ -662,7 +788,7 @@ static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* Configure the arbitration mechanisms for VC requests */ ipu6_configure_vc_mechanism(isp); - val = readl(isp->base + BUTTRESS_REG_SKU); + val = readl(isp->base + isp->buttress.regs->sku_id); sku_id = FIELD_GET(GENMASK(6, 4), val); version = FIELD_GET(GENMASK(3, 0), val); dev_info(dev, "IPU%u-v%u[%x] hardware version %d\n", version, sku_id, @@ -680,10 +806,21 @@ out_free_irq: out_ipu6_rpm_put: pm_runtime_put_sync(&isp->psys->auxdev.dev); out_ipu6_bus_del_devices: + dir = IS_IPU7(isp) ? DMA_BIDIRECTIONAL : DMA_TO_DEVICE; if (!IS_ERR_OR_NULL(isp->psys)) { ipu6_cpd_free_pkg_dir(isp->psys); - ipu6_buttress_unmap_fw_image(isp->psys, &isp->psys->fw_sgt); + if (isp->psys->fw_sgt.nents) + ipu6_unmap_fw_region(isp->psys, dir); + } + if (!IS_ERR_OR_NULL(isp->isys)) { + ipu6_cpd_free_pkg_dir(isp->isys); + if (isp->isys->fw_sgt.nents) + ipu6_unmap_fw_region(isp->isys, dir); } + + vfree(isp->fw_code_region); + isp->fw_code_region = NULL; + if (!IS_ERR_OR_NULL(isp->psys) && !IS_ERR_OR_NULL(isp->psys->mmu)) ipu6_mmu_cleanup(isp->psys->mmu); if (!IS_ERR_OR_NULL(isp->isys) && !IS_ERR_OR_NULL(isp->isys->mmu)) @@ -699,13 +836,23 @@ buttress_exit: static void ipu6_pci_remove(struct pci_dev *pdev) { struct ipu6_device *isp = pci_get_drvdata(pdev); - struct ipu6_mmu *isys_mmu = isp->isys->mmu; - struct ipu6_mmu *psys_mmu = isp->psys->mmu; + unsigned long dir; devm_free_irq(&pdev->dev, pdev->irq, isp); + + dir = IS_IPU7(isp) ? DMA_BIDIRECTIONAL : DMA_TO_DEVICE; ipu6_cpd_free_pkg_dir(isp->psys); + ipu6_unmap_fw_region(isp->psys, dir); + + if (isp->isys) { + ipu6_cpd_free_pkg_dir(isp->isys); + if (isp->isys->fw_sgt.nents) + ipu6_unmap_fw_region(isp->isys, dir); + } + + vfree(isp->fw_code_region); + isp->fw_code_region = NULL; - ipu6_buttress_unmap_fw_image(isp->psys, &isp->psys->fw_sgt); ipu6_buttress_exit(isp); ipu6_bus_del_devices(pdev); @@ -715,8 +862,10 @@ static void ipu6_pci_remove(struct pci_dev *pdev) release_firmware(isp->cpd_fw); - ipu6_mmu_cleanup(psys_mmu); - ipu6_mmu_cleanup(isys_mmu); + if (isp->psys) + ipu6_mmu_cleanup(isp->psys->mmu); + if (isp->isys) + ipu6_mmu_cleanup(isp->isys->mmu); } static void ipu6_pci_reset_prepare(struct pci_dev *pdev) @@ -754,7 +903,6 @@ static int ipu6_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct ipu6_device *isp = pci_get_drvdata(pdev); - struct ipu6_buttress *b = &isp->buttress; int ret; /* Configure the arbitration mechanisms for VC requests */ @@ -766,7 +914,7 @@ static int ipu6_resume(struct device *dev) ipu6_buttress_restore(isp); - ret = ipu6_buttress_ipc_reset(isp, &b->cse); + ret = ipu6_buttress_ipc_reset(isp); if (ret) dev_err(&isp->pdev->dev, "IPC reset protocol failed!\n"); @@ -795,10 +943,8 @@ static int ipu6_runtime_resume(struct device *dev) ipu6_buttress_restore(isp); if (isp->need_ipc_reset) { - struct ipu6_buttress *b = &isp->buttress; - isp->need_ipc_reset = false; - ret = ipu6_buttress_ipc_reset(isp, &b->cse); + ret = ipu6_buttress_ipc_reset(isp); if (ret) dev_err(&isp->pdev->dev, "IPC reset protocol failed\n"); } diff --git a/drivers/media/pci/intel/ipu6/ipu6.h b/drivers/media/pci/intel/ipu6/ipu6.h index 92e3c3414c91..72c5063d5198 100644 --- a/drivers/media/pci/intel/ipu6/ipu6.h +++ b/drivers/media/pci/intel/ipu6/ipu6.h @@ -16,46 +16,32 @@ struct ipu6_bus_device; #define IPU6_NAME "intel-ipu6" #define IPU6_MEDIA_DEV_MODEL_NAME "ipu6" +#define IPU7_MEDIA_DEV_MODEL_NAME "ipu7" +#define IPU7P5_MEDIA_DEV_MODEL_NAME "ipu7.5" #define IPU6SE_FIRMWARE_NAME "intel/ipu/ipu6se_fw.bin" #define IPU6EP_FIRMWARE_NAME "intel/ipu/ipu6ep_fw.bin" #define IPU6_FIRMWARE_NAME "intel/ipu/ipu6_fw.bin" #define IPU6EPMTL_FIRMWARE_NAME "intel/ipu/ipu6epmtl_fw.bin" #define IPU6EPADLN_FIRMWARE_NAME "intel/ipu/ipu6epadln_fw.bin" - -enum ipu6_version { - IPU6_VER_INVALID = 0, - IPU6_VER_6 = 1, - IPU6_VER_6SE = 3, - IPU6_VER_6EP = 5, - IPU6_VER_6EP_MTL = 6, -}; - -/* - * IPU6 - TGL - * IPU6SE - JSL - * IPU6EP - ADL/RPL - * IPU6EP_MTL - MTL - */ -static inline bool is_ipu6se(u8 hw_ver) -{ - return hw_ver == IPU6_VER_6SE; -} - -static inline bool is_ipu6ep(u8 hw_ver) -{ - return hw_ver == IPU6_VER_6EP; -} - -static inline bool is_ipu6ep_mtl(u8 hw_ver) -{ - return hw_ver == IPU6_VER_6EP_MTL; -} - -static inline bool is_ipu6_tgl(u8 hw_ver) -{ - return hw_ver == IPU6_VER_6; -} +#define IPU7_FIRMWARE_NAME "intel/ipu/ipu7_fw.bin" +#define IPU7P5_FIRMWARE_NAME "intel/ipu/ipu7ptl_fw.bin" + +#define IPU_VERSION_6 BIT(0) /* TGL */ +#define IPU_VERSION_6SE BIT(1) /* JSL */ +#define IPU_VERSION_6EP BIT(2) /* ADL/RPL */ +#define IPU_VERSION_6EP_MTL BIT(3) /* MTL */ +#define IPU_VERSION_7 BIT(4) /* LNL */ +#define IPU_VERSION_7P5 BIT(5) /* PTL */ + +#define IS_IPU6_TGL(isp) ((isp)->hw_ver & IPU_VERSION_6) +#define IS_IPU6SE(isp) ((isp)->hw_ver & IPU_VERSION_6SE) +#define IS_IPU6EP(isp) ((isp)->hw_ver & IPU_VERSION_6EP) +#define IS_IPU6EP_MTL(isp) ((isp)->hw_ver & IPU_VERSION_6EP_MTL) +#define IS_IPU7(isp) ((isp)->hw_ver & \ + (IPU_VERSION_7 | IPU_VERSION_7P5)) +#define IS_IPU7_MTL(isp) ((isp)->hw_ver & IPU_VERSION_7) +#define IS_IPU7P5(isp) ((isp)->hw_ver & IPU_VERSION_7P5) /* * ISYS DMA can overshoot. For higher resolutions over allocation is one line @@ -80,14 +66,21 @@ struct ipu6_device { const struct firmware *cpd_fw; const char *cpd_fw_name; u32 cpd_metadata_cmpnt_size; + const char *model_name; void __iomem *base; + void __iomem *pb_base; bool need_ipc_reset; bool secure_mode; u8 hw_ver; bool bus_ready_to_probe; + u32 *fw_code_region; }; +#define IPU_PSYS 0 +#define IPU_ISYS 1 +#define IPU_SUBSYS_NUM 2 + #define IPU6_ISYS_NAME "isys" #define IPU6_PSYS_NAME "psys" @@ -134,141 +127,6 @@ struct ipu6_device { #define IPU6_BTRS_ARB_STALL_MODE_VC1 \ IPU6_BTRS_ARB_MODE_TYPE_REARB -/* - * MMU Invalidation HW bug workaround by ZLW mechanism - * - * Old IPU6 MMUV2 has a bug in the invalidation mechanism which might result in - * wrong translation or replication of the translation. This will cause data - * corruption. So we cannot directly use the MMU V2 invalidation registers - * to invalidate the MMU. Instead, whenever an invalidate is called, we need to - * clear the TLB by evicting all the valid translations by filling it with trash - * buffer (which is guaranteed not to be used by any other processes). ZLW is - * used to fill the L1 and L2 caches with the trash buffer translations. ZLW - * or Zero length write, is pre-fetch mechanism to pre-fetch the pages in - * advance to the L1 and L2 caches without triggering any memory operations. - * - * In MMU V2, L1 -> 16 streams and 64 blocks, maximum 16 blocks per stream - * One L1 block has 16 entries, hence points to 16 * 4K pages - * L2 -> 16 streams and 32 blocks. 2 blocks per streams - * One L2 block maps to 1024 L1 entries, hence points to 4MB address range - * 2 blocks per L2 stream means, 1 stream points to 8MB range - * - * As we need to clear the caches and 8MB being the biggest cache size, we need - * to have trash buffer which points to 8MB address range. As these trash - * buffers are not used for any memory transactions, we need only the least - * amount of physical memory. So we reserve 8MB IOVA address range but only - * one page is reserved from physical memory. Each of this 8MB IOVA address - * range is then mapped to the same physical memory page. - */ -/* One L2 entry maps 1024 L1 entries and one L1 entry per page */ -#define IPU6_MMUV2_L2_RANGE (1024 * PAGE_SIZE) -/* Max L2 blocks per stream */ -#define IPU6_MMUV2_MAX_L2_BLOCKS 2 -/* Max L1 blocks per stream */ -#define IPU6_MMUV2_MAX_L1_BLOCKS 16 -#define IPU6_MMUV2_TRASH_RANGE (IPU6_MMUV2_L2_RANGE * IPU6_MMUV2_MAX_L2_BLOCKS) -/* Entries per L1 block */ -#define MMUV2_ENTRIES_PER_L1_BLOCK 16 -#define MMUV2_TRASH_L1_BLOCK_OFFSET (MMUV2_ENTRIES_PER_L1_BLOCK * PAGE_SIZE) -#define MMUV2_TRASH_L2_BLOCK_OFFSET IPU6_MMUV2_L2_RANGE - -/* - * In some of the IPU6 MMUs, there is provision to configure L1 and L2 page - * table caches. Both these L1 and L2 caches are divided into multiple sections - * called streams. There is maximum 16 streams for both caches. Each of these - * sections are subdivided into multiple blocks. When nr_l1streams = 0 and - * nr_l2streams = 0, means the MMU is of type MMU_V1 and do not support - * L1/L2 page table caches. - * - * L1 stream per block sizes are configurable and varies per usecase. - * L2 has constant block sizes - 2 blocks per stream. - * - * MMU1 support pre-fetching of the pages to have less cache lookup misses. To - * enable the pre-fetching, MMU1 AT (Address Translator) device registers - * need to be configured. - * - * There are four types of memory accesses which requires ZLW configuration. - * ZLW(Zero Length Write) is a mechanism to enable VT-d pre-fetching on IOMMU. - * - * 1. Sequential Access or 1D mode - * Set ZLW_EN -> 1 - * set ZLW_PAGE_CROSS_1D -> 1 - * Set ZLW_N to "N" pages so that ZLW will be inserte N pages ahead where - * N is pre-defined and hardcoded in the platform data - * Set ZLW_2D -> 0 - * - * 2. ZLW 2D mode - * Set ZLW_EN -> 1 - * set ZLW_PAGE_CROSS_1D -> 1, - * Set ZLW_N -> 0 - * Set ZLW_2D -> 1 - * - * 3. ZLW Enable (no 1D or 2D mode) - * Set ZLW_EN -> 1 - * set ZLW_PAGE_CROSS_1D -> 0, - * Set ZLW_N -> 0 - * Set ZLW_2D -> 0 - * - * 4. ZLW disable - * Set ZLW_EN -> 0 - * set ZLW_PAGE_CROSS_1D -> 0, - * Set ZLW_N -> 0 - * Set ZLW_2D -> 0 - * - * To configure the ZLW for the above memory access, four registers are - * available. Hence to track these four settings, we have the following entries - * in the struct ipu6_mmu_hw. Each of these entries are per stream and - * available only for the L1 streams. - * - * a. l1_zlw_en -> To track zlw enabled per stream (ZLW_EN) - * b. l1_zlw_1d_mode -> Track 1D mode per stream. ZLW inserted at page boundary - * c. l1_ins_zlw_ahead_pages -> to track how advance the ZLW need to be inserted - * Insert ZLW request N pages ahead address. - * d. l1_zlw_2d_mode -> To track 2D mode per stream (ZLW_2D) - * - * - * Currently L1/L2 streams, blocks, AT ZLW configurations etc. are pre-defined - * as per the usecase specific calculations. Any change to this pre-defined - * table has to happen in sync with IPU6 FW. - */ -struct ipu6_mmu_hw { - union { - unsigned long offset; - void __iomem *base; - }; - u32 info_bits; - u8 nr_l1streams; - /* - * L1 has variable blocks per stream - total of 64 blocks and maximum of - * 16 blocks per stream. Configurable by using the block start address - * per stream. Block start address is calculated from the block size - */ - u8 l1_block_sz[IPU6_MMU_MAX_TLB_L1_STREAMS]; - /* Is ZLW is enabled in each stream */ - bool l1_zlw_en[IPU6_MMU_MAX_TLB_L1_STREAMS]; - bool l1_zlw_1d_mode[IPU6_MMU_MAX_TLB_L1_STREAMS]; - u8 l1_ins_zlw_ahead_pages[IPU6_MMU_MAX_TLB_L1_STREAMS]; - bool l1_zlw_2d_mode[IPU6_MMU_MAX_TLB_L1_STREAMS]; - - u32 l1_stream_id_reg_offset; - u32 l2_stream_id_reg_offset; - - u8 nr_l2streams; - /* - * L2 has fixed 2 blocks per stream. Block address is calculated - * from the block size - */ - u8 l2_block_sz[IPU6_MMU_MAX_TLB_L2_STREAMS]; - /* flag to track if WA is needed for successive invalidate HW bug */ - bool insert_read_before_invalidate; -}; - -struct ipu6_mmu_pdata { - u32 nr_mmus; - struct ipu6_mmu_hw mmu_hw[IPU6_MMU_MAX_DEVICES]; - int mmid; -}; - struct ipu6_isys_csi2_pdata { void __iomem *base; }; @@ -283,6 +141,8 @@ struct ipu6_isys_internal_csi2_pdata { u32 ctrl0_irq_lnp; u32 ctrl0_irq_status; u32 fw_access_port_ofs; + /* IPU7-specific field */ + u32 gpreg; }; struct ipu6_isys_internal_tpg_pdata { @@ -293,8 +153,6 @@ struct ipu6_isys_internal_tpg_pdata { struct ipu6_hw_variants { unsigned long offset; - u32 nr_mmus; - struct ipu6_mmu_hw mmu_hw[IPU6_MMU_MAX_DEVICES]; u8 cdc_fifos; u8 cdc_fifo_threshold[IPU6_MAX_VC_IOSF_PORTS]; u32 dmem_offset; diff --git a/drivers/media/pci/intel/ipu6/ipu7-boot.c b/drivers/media/pci/intel/ipu6/ipu7-boot.c new file mode 100644 index 000000000000..952c73efd237 --- /dev/null +++ b/drivers/media/pci/intel/ipu6/ipu7-boot.c @@ -0,0 +1,405 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 - 2026 Intel Corporation + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/iopoll.h> +#include <linux/types.h> + +#include "ipu6.h" +#include "ipu6-bus.h" +#include "ipu6-buttress.h" +#include "ipu6-dma.h" +#include "ipu6-isys.h" +#include "ipu6-platform-buttress-regs.h" +#include "ipu7-boot.h" +#include "ipu7-platform-regs.h" + +#define IPU7_FW_START_STOP_TIMEOUT 2000 +#define IPU7_BOOT_CELL_RESET_TIMEOUT (2 * USEC_PER_SEC) +#define IPU7_BOOT_STATE_CRITICAL(s) (((s) & 0xffff0000U) == 0xdead0000U) +#define IPU7_BOOT_STATE_READY(s) ((s) == 0x57a7e100U) +#define IPU7_BOOT_STATE_INACTIVE(s) ((s) == 0x57a7e300U) +#define IPU7_BUTTRESS_REG_FW_BOOT_PARAMS0 0x4000 +#define IPU7_BUTTRESS_FW_BOOT_PARAMS_ENTRY(i) \ + (IPU7_BUTTRESS_REG_FW_BOOT_PARAMS0 + ((i) * 4U)) + +struct boot_regs { + u32 base; + u32 dmem_address; + u32 status_ctrl_reg; + u32 fw_start_address_reg; + u32 fw_code_base_reg; +}; + +enum ipu7_boot_reg_id { + IPU7_FW_BOOT_CONFIG_ID = 0, + IPU7_FW_BOOT_STATE_ID = 1, + IPU7_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID = 2, + IPU7_FW_BOOT_UNTRUSTED_ADDR_MIN_ID = 3, + IPU7_FW_BOOT_MESSAGING_VERSION_ID = 4, + IPU7_FW_BOOT_ID_MAX, +}; + +enum ipu7_boot_state { + IPU7_FW_BOOT_STATE_UNINIT = 0x57a7e000U, + IPU7_FW_BOOT_STATE_READY = 0x57a7e100U, + IPU7_FW_BOOT_STATE_SHUTDOWN_CMD = 0x57a7f001U, + IPU7_FW_BOOT_STATE_INACTIVE = 0x57a7e300U, +}; + +static const struct boot_regs boot_regs[IPU_SUBSYS_NUM] = { + [IPU_ISYS] = { + .dmem_address = IPU7_ISYS_DMEM_OFFSET, + .status_ctrl_reg = IPU7_BUTTRESS_REG_ISYS_UCX_CTRL_STATUS, + .fw_start_address_reg = IPU7_BUTTRESS_REG_ISYS_UCX_START_ADDR, + .fw_code_base_reg = IPU7_IS_UC_CTRL_BASE + }, + [IPU_PSYS] = { + .dmem_address = IPU7_PSYS_DMEM_OFFSET, + .status_ctrl_reg = IPU7_BUTTRESS_REG_PSYS_UCX_CTRL_STATUS, + .fw_start_address_reg = IPU7_BUTTRESS_REG_PSYS_UCX_START_ADDR, + .fw_code_base_reg = IPU7_PS_UC_CTRL_BASE + } +}; + +static u32 get_fw_boot_reg_addr(const struct ipu6_bus_device *adev, + enum ipu7_boot_reg_id reg) +{ + u32 base = (adev->ctrl->subsys_id == IPU_ISYS) ? + 0U : (u32)IPU7_FW_BOOT_ID_MAX; + + return IPU7_BUTTRESS_FW_BOOT_PARAMS_ENTRY(base + (u32)reg); +} + +static void write_fw_boot_param(const struct ipu6_bus_device *adev, + enum ipu7_boot_reg_id reg, + u32 val) +{ + void __iomem *base = adev->isp->base; + + dev_dbg(&adev->auxdev.dev, + "write boot param reg: %d addr: %x val: 0x%x\n", + reg, get_fw_boot_reg_addr(adev, reg), val); + writel(val, base + get_fw_boot_reg_addr(adev, reg)); +} + +static u32 read_fw_boot_param(const struct ipu6_bus_device *adev, + enum ipu7_boot_reg_id reg) +{ + void __iomem *base = adev->isp->base; + + return readl(base + get_fw_boot_reg_addr(adev, reg)); +} + +static int ipu7_boot_cell_reset(const struct ipu6_bus_device *adev) +{ + const struct device *dev = &adev->auxdev.dev; + const struct boot_regs *regs = &boot_regs[adev->ctrl->subsys_id]; + u32 ucx_ctrl_status = regs->status_ctrl_reg; + u32 timeout = IPU7_BOOT_CELL_RESET_TIMEOUT; + void __iomem *base = adev->isp->base; + u32 val, val2; + int ret; + + val = readl(base + ucx_ctrl_status); + val |= IPU7_UCX_CTL_RESET; + val &= ~IPU7_UCX_CTL_RUN; + + writel(val, base + ucx_ctrl_status); + + ret = readl_poll_timeout(base + ucx_ctrl_status, val2, + (val2 & 0x3U) == (val & 0x3U), 100, timeout); + if (ret) { + dev_err(dev, "cell enter reset timeout. status: 0x%x\n", val2); + return -ETIMEDOUT; + } + + val = readl(base + ucx_ctrl_status); + val &= ~(IPU7_UCX_CTL_RESET | IPU7_UCX_CTL_RUN); + writel(val, base + ucx_ctrl_status); + + ret = readl_poll_timeout(base + ucx_ctrl_status, val2, + (val2 & 0x3U) == (val & 0x3U), 100, timeout); + if (ret) { + dev_err(dev, "cell exit reset timeout. status: 0x%x\n", val2); + return -ETIMEDOUT; + } + + return 0; +} + +static void ipu7_boot_cell_start(const struct ipu6_bus_device *adev) +{ + const struct boot_regs *regs = &boot_regs[adev->ctrl->subsys_id]; + void __iomem *base = adev->isp->base; + u32 val; + + val = readl(base + regs->status_ctrl_reg); + WARN_ON(val & (IPU7_UCX_CTL_RESET | IPU7_UCX_CTL_RUN)); + + val &= ~IPU7_UCX_CTL_RESET; + val |= IPU7_UCX_CTL_RUN; + writel(val, base + regs->status_ctrl_reg); +} + +static void ipu7_boot_cell_stop(const struct ipu6_bus_device *adev) +{ + const struct boot_regs *regs = &boot_regs[adev->ctrl->subsys_id]; + void __iomem *base = adev->isp->base; + u32 val; + + val = readl(base + regs->status_ctrl_reg); + val &= ~IPU7_UCX_CTL_RUN; + writel(val, base + regs->status_ctrl_reg); + + /* Wait for uC transactions complete */ + usleep_range(10, 20); + + val = readl(base + regs->status_ctrl_reg); + val |= IPU7_UCX_CTL_RESET; + writel(val, base + regs->status_ctrl_reg); +} + +static int ipu7_boot_cell_init(const struct ipu6_bus_device *adev) +{ + const struct boot_regs *regs = &boot_regs[adev->ctrl->subsys_id]; + struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); + struct ipu7_fw_com_context *fwctx = isys->fwctx; + void __iomem *base = adev->isp->base; + + writel(fwctx->fw_entry, base + regs->fw_start_address_reg); + + return ipu7_boot_cell_reset(adev); +} + +static void init_cfg_versions(struct ipu7_boot_abi_cfg *boot_cfg, u32 length, u8 major) +{ + boot_cfg->length = length; + boot_cfg->config_version.major = 1U; + boot_cfg->config_version.minor = 0U; + boot_cfg->config_version.subminor = 0U; + boot_cfg->config_version.patch = 0U; + + boot_cfg->client_version_support.num_versions = 1U; + boot_cfg->client_version_support.versions[0].major = major; + boot_cfg->client_version_support.versions[0].minor = 0U; + boot_cfg->client_version_support.versions[0].subminor = 0U; + boot_cfg->client_version_support.versions[0].patch = 0U; +} + +int ipu6_ipu7_init_boot_config(struct ipu6_bus_device *adev, + struct ipu7_fw_com_queue_config *qconfigs, + int num_queues, u32 uc_freq, + dma_addr_t subsys_config, u8 major) +{ + struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); + struct ipu7_fw_com_context *fwctx = isys->fwctx; + struct ipu7_boot_abi_cfg *boot_config; + struct ipu7_fw_com_queue_params_config *cfgs; + struct device *dev = &adev->auxdev.dev; + u32 total_queue_size_aligned = 0; + dma_addr_t queue_mem_dma_ptr; + void *queue_mem_ptr; + unsigned int i; + + /* Allocate boot config. */ + fwctx->boot_config_size = + sizeof(*cfgs) * num_queues + sizeof(*boot_config); + fwctx->boot_config = ipu6_dma_alloc(adev, fwctx->boot_config_size, + &fwctx->boot_config_dma_addr, + GFP_KERNEL, 0); + if (!fwctx->boot_config) { + dev_err(dev, "Failed to allocate boot config.\n"); + return -ENOMEM; + } + + boot_config = fwctx->boot_config; + memset(boot_config, 0, sizeof(*boot_config)); + init_cfg_versions(boot_config, fwctx->boot_config_size, major); + boot_config->subsys_config = subsys_config; + + boot_config->uc_tile_frequency = uc_freq; + boot_config->uc_tile_frequency_units = 0; + boot_config->fw_com_config.max_output_queues = + fwctx->num_output_queues; + boot_config->fw_com_config.max_input_queues = + fwctx->num_input_queues; + + ipu6_dma_sync_single(adev, fwctx->boot_config_dma_addr, + fwctx->boot_config_size); + + for (i = 0; i < num_queues; i++) { + u32 queue_size = qconfigs[i].max_capacity * + qconfigs[i].token_size_in_bytes; + + queue_size = ALIGN(queue_size, 64U); + total_queue_size_aligned += queue_size; + qconfigs[i].queue_size = queue_size; + } + + /* Allocate queue memory */ + fwctx->queue_mem = ipu6_dma_alloc(adev, total_queue_size_aligned, + &fwctx->queue_mem_dma_addr, + GFP_KERNEL, 0); + if (!fwctx->queue_mem) { + dev_err(dev, "Failed to allocate queue memory.\n"); + return -ENOMEM; + } + fwctx->queue_mem_size = total_queue_size_aligned; + + cfgs = ipu7_fw_com_get_queue_config(&boot_config->fw_com_config); + queue_mem_ptr = fwctx->queue_mem; + queue_mem_dma_ptr = fwctx->queue_mem_dma_addr; + for (i = 0; i < num_queues; i++) { + cfgs[i].token_array_mem = queue_mem_dma_ptr; + cfgs[i].max_capacity = qconfigs[i].max_capacity; + cfgs[i].token_size_in_bytes = qconfigs[i].token_size_in_bytes; + qconfigs[i].token_array_mem = queue_mem_ptr; + queue_mem_dma_ptr += qconfigs[i].queue_size; + queue_mem_ptr += qconfigs[i].queue_size; + } + + ipu6_dma_sync_single(adev, fwctx->queue_mem_dma_addr, + total_queue_size_aligned); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ipu6_ipu7_init_boot_config, "INTEL_IPU6"); + +void ipu6_ipu7_release_boot_config(struct ipu6_bus_device *adev) +{ + struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); + struct ipu7_fw_com_context *fwctx; + + if (!isys || !isys->fwctx) + return; + + fwctx = isys->fwctx; + + if (fwctx->queue_mem) { + ipu6_dma_free(adev, fwctx->queue_mem_size, + fwctx->queue_mem, + fwctx->queue_mem_dma_addr, 0); + fwctx->queue_mem = NULL; + fwctx->queue_mem_dma_addr = 0; + } + + if (fwctx->boot_config) { + ipu6_dma_free(adev, fwctx->boot_config_size, + fwctx->boot_config, + fwctx->boot_config_dma_addr, 0); + fwctx->boot_config = NULL; + fwctx->boot_config_dma_addr = 0; + } +} +EXPORT_SYMBOL_NS_GPL(ipu6_ipu7_release_boot_config, "INTEL_IPU6"); + +int ipu6_ipu7_boot_start_fw(const struct ipu6_bus_device *adev) +{ + const struct device *dev = &adev->auxdev.dev; + struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); + struct ipu7_fw_com_context *fwctx = isys->fwctx; + u32 timeout = IPU7_FW_START_STOP_TIMEOUT; + void __iomem *base = adev->isp->base; + u32 boot_state, last_boot_state; + u32 indices_addr, msg_ver, id; + int ret; + + ret = ipu7_boot_cell_init(adev); + if (ret) + return ret; + + /* store "uninit" state to boot state reg */ + write_fw_boot_param(adev, IPU7_FW_BOOT_STATE_ID, + IPU7_FW_BOOT_STATE_UNINIT); + /* Set registers to zero, recommended for diagnostics. */ + write_fw_boot_param(adev, + IPU7_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID, 0); + write_fw_boot_param(adev, IPU7_FW_BOOT_MESSAGING_VERSION_ID, 0); + /* store firmware configuration address */ + write_fw_boot_param(adev, IPU7_FW_BOOT_CONFIG_ID, + fwctx->boot_config_dma_addr); + + ipu7_boot_cell_start(adev); + + last_boot_state = IPU7_FW_BOOT_STATE_UNINIT; + while (timeout--) { + boot_state = read_fw_boot_param(adev, + IPU7_FW_BOOT_STATE_ID); + if (boot_state != last_boot_state) { + dev_dbg(dev, "boot state changed from 0x%x to 0x%x\n", + last_boot_state, boot_state); + last_boot_state = boot_state; + } + if (IPU7_BOOT_STATE_CRITICAL(boot_state) || + IPU7_BOOT_STATE_READY(boot_state)) + break; + usleep_range(1000, 1200); + } + + if (IPU7_BOOT_STATE_CRITICAL(boot_state)) { + dev_err(dev, "critical boot state error 0x%x\n", boot_state); + return -EINVAL; + } else if (!IPU7_BOOT_STATE_READY(boot_state)) { + dev_err(dev, "fw boot timeout. state: 0x%x\n", boot_state); + return -ETIMEDOUT; + } + dev_dbg(dev, "fw boot done.\n"); + + id = IPU7_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID; + indices_addr = read_fw_boot_param(adev, id); + fwctx->queue_indices = base + indices_addr; + dev_dbg(dev, "fw queue indices offset is 0x%x\n", indices_addr); + + msg_ver = read_fw_boot_param(adev, + IPU7_FW_BOOT_MESSAGING_VERSION_ID); + dev_dbg(dev, "ipu message version is 0x%08x\n", msg_ver); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ipu6_ipu7_boot_start_fw, "INTEL_IPU6"); + +int ipu6_ipu7_boot_stop_fw(const struct ipu6_bus_device *adev) +{ + const struct device *dev = &adev->auxdev.dev; + u32 timeout = IPU7_FW_START_STOP_TIMEOUT; + u32 boot_state; + + boot_state = read_fw_boot_param(adev, IPU7_FW_BOOT_STATE_ID); + if (IPU7_BOOT_STATE_CRITICAL(boot_state) || + !IPU7_BOOT_STATE_READY(boot_state)) { + dev_err(dev, "fw not ready for shutdown, state 0x%x\n", + boot_state); + return -EBUSY; + } + + /* Issue shutdown to start shutdown process */ + dev_dbg(dev, "stopping fw...\n"); + write_fw_boot_param(adev, IPU7_FW_BOOT_STATE_ID, + IPU7_FW_BOOT_STATE_SHUTDOWN_CMD); + while (timeout--) { + boot_state = read_fw_boot_param(adev, + IPU7_FW_BOOT_STATE_ID); + if (IPU7_BOOT_STATE_CRITICAL(boot_state) || + IPU7_BOOT_STATE_INACTIVE(boot_state)) + break; + usleep_range(1000, 1200); + } + + if (IPU7_BOOT_STATE_CRITICAL(boot_state)) { + dev_err(dev, "critical boot state error 0x%x\n", boot_state); + return -EINVAL; + } else if (!IPU7_BOOT_STATE_INACTIVE(boot_state)) { + dev_err(dev, "stop fw timeout. state: 0x%x\n", boot_state); + return -ETIMEDOUT; + } + + ipu7_boot_cell_stop(adev); + dev_dbg(dev, "stop fw done.\n"); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ipu6_ipu7_boot_stop_fw, "INTEL_IPU6"); diff --git a/drivers/media/pci/intel/ipu6/ipu7-boot.h b/drivers/media/pci/intel/ipu6/ipu7-boot.h new file mode 100644 index 000000000000..f2172b53bed5 --- /dev/null +++ b/drivers/media/pci/intel/ipu6/ipu7-boot.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2026 Intel Corporation */ + +#ifndef IPU7_BOOT_H +#define IPU7_BOOT_H + +#include "ipu7-fw-com.h" + +#define IPU7_BOOT_MSG_VER_MAX_ENTRIES 3U + +struct ipu7_boot_abi_version { + u8 patch; + u8 subminor; + u8 minor; + u8 major; +}; + +struct ipu7_boot_abi_msg_versions { + u8 num_versions; + u8 reserved[3]; + struct ipu7_boot_abi_version versions[IPU7_BOOT_MSG_VER_MAX_ENTRIES]; +}; + +struct ipu7_boot_abi_cfg { + u32 length; + struct ipu7_boot_abi_version config_version; + struct ipu7_boot_abi_msg_versions client_version_support; + u32 pkg_dir; + u32 subsys_config; + u32 uc_tile_frequency; + u16 checksum; + u8 uc_tile_frequency_units; + u8 padding[1]; + u32 reserved[58]; + struct ipu7_fw_com_config fw_com_config; +} __packed; + +int ipu6_ipu7_init_boot_config(struct ipu6_bus_device *adev, + struct ipu7_fw_com_queue_config *qconfigs, + int num_queues, u32 uc_freq, + dma_addr_t subsys_config, u8 major); +void ipu6_ipu7_release_boot_config(struct ipu6_bus_device *adev); +int ipu6_ipu7_boot_start_fw(const struct ipu6_bus_device *adev); +int ipu6_ipu7_boot_stop_fw(const struct ipu6_bus_device *adev); + +#endif diff --git a/drivers/media/pci/intel/ipu6/ipu7-fw-com.c b/drivers/media/pci/intel/ipu6/ipu7-fw-com.c new file mode 100644 index 000000000000..7dd1e683aa92 --- /dev/null +++ b/drivers/media/pci/intel/ipu6/ipu7-fw-com.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Intel Corporation + */ + +#include <linux/io.h> + +#include "ipu7-fw-com.h" + +static void __iomem *ipu7_fw_com_get_indices(struct ipu7_fw_com_context *ctx, + u32 q) +{ + return ctx->queue_indices + (q * sizeof(struct ipu7_fw_com_queue_indices)); +} + +void *ipu7_fw_com_get_token(struct ipu7_fw_com_context *ctx, int q) +{ + struct ipu7_fw_com_queue_config *queue_params = &ctx->queue_configs[q]; + void __iomem *queue_indices = ipu7_fw_com_get_indices(ctx, q); + u32 write_index = readl(queue_indices + + offsetof(struct ipu7_fw_com_queue_indices, + write_index)); + u32 read_index = readl(queue_indices + + offsetof(struct ipu7_fw_com_queue_indices, + read_index)); + void *token = NULL; + + if (q < ctx->num_output_queues) { + /* Output queue */ + bool empty = (write_index == read_index); + + if (!empty) + token = queue_params->token_array_mem + + read_index * + queue_params->token_size_in_bytes; + } else { + /* Input queue */ + bool full = (read_index == ((write_index + 1U) % + (u32)queue_params->max_capacity)); + + if (!full) + token = queue_params->token_array_mem + + write_index * queue_params->token_size_in_bytes; + } + return token; +} +EXPORT_SYMBOL_NS_GPL(ipu7_fw_com_get_token, "INTEL_IPU6"); + +void ipu7_fw_com_put_token(struct ipu7_fw_com_context *ctx, int q) +{ + struct ipu7_fw_com_queue_config *queue_params = &ctx->queue_configs[q]; + void __iomem *queue_indices = ipu7_fw_com_get_indices(ctx, q); + u32 offset, index; + + if (q < ctx->num_output_queues) + /* Output queue */ + offset = offsetof(struct ipu7_fw_com_queue_indices, read_index); + + else + /* Input queue */ + offset = offsetof(struct ipu7_fw_com_queue_indices, write_index); + + index = readl(queue_indices + offset); + writel((index + 1U) % queue_params->max_capacity, + queue_indices + offset); +} +EXPORT_SYMBOL_NS_GPL(ipu7_fw_com_put_token, "INTEL_IPU6"); + +struct ipu7_fw_com_queue_params_config * +ipu7_fw_com_get_queue_config(struct ipu7_fw_com_config *config) +{ + return (struct ipu7_fw_com_queue_params_config *)(&config[1]); +} +EXPORT_SYMBOL_NS_GPL(ipu7_fw_com_get_queue_config, "INTEL_IPU6"); diff --git a/drivers/media/pci/intel/ipu6/ipu7-fw-com.h b/drivers/media/pci/intel/ipu6/ipu7-fw-com.h new file mode 100644 index 000000000000..097eaab99547 --- /dev/null +++ b/drivers/media/pci/intel/ipu6/ipu7-fw-com.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2026 Intel Corporation */ + +#ifndef IPU7_FW_COM_H +#define IPU7_FW_COM_H + +#include <linux/types.h> + +struct ipu7_fw_com_queue_config { + void *token_array_mem; + u32 queue_size; + u16 token_size_in_bytes; + u16 max_capacity; +}; + +struct ipu7_fw_com_context { + u16 num_input_queues; + u16 num_output_queues; + struct ipu7_fw_com_queue_config *queue_configs; + void __iomem *queue_indices; + dma_addr_t queue_mem_dma_addr; + void *queue_mem; + u32 queue_mem_size; + struct ipu7_boot_abi_cfg *boot_config; + dma_addr_t boot_config_dma_addr; + u32 boot_config_size; + u32 fw_entry; + struct ipu7_insys_config *fw_config; + dma_addr_t fw_config_dma_addr; +}; + +struct ipu7_fw_com_queue_params_config { + u32 token_array_mem; + u16 token_size_in_bytes; + u16 max_capacity; +}; + +struct ipu7_fw_com_config { + u16 max_output_queues; + u16 max_input_queues; +}; + +struct ipu7_fw_com_queue_indices { + u32 read_index; + u32 write_index; +}; + +void ipu7_fw_com_put_token(struct ipu7_fw_com_context *ctx, int q); +void *ipu7_fw_com_get_token(struct ipu7_fw_com_context *ctx, int q); +struct ipu7_fw_com_queue_params_config * +ipu7_fw_com_get_queue_config(struct ipu7_fw_com_config *config); + +#endif /* IPU7_FW_COM_H */ diff --git a/drivers/media/pci/intel/ipu6/ipu7-fw-isys.c b/drivers/media/pci/intel/ipu6/ipu7-fw-isys.c new file mode 100644 index 000000000000..0876cc54faa7 --- /dev/null +++ b/drivers/media/pci/intel/ipu6/ipu7-fw-isys.c @@ -0,0 +1,796 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Intel Corporation + */ + +#include <linux/cleanup.h> +#include <linux/cacheflush.h> +#include <linux/pm_runtime.h> + +#include "ipu6-bus.h" +#include "ipu6-dma.h" +#include "ipu6-isys.h" +#include "ipu6-platform-regs.h" +#include "ipu7-boot.h" +#include "ipu7-fw-com.h" +#include "ipu7-fw-isys.h" +#include "ipu7-isys-csi2-regs.h" +#include "ipu7-platform-regs.h" + +static void ipu7_fw_isys_cleanup(struct ipu6_isys *isys) +{ + struct ipu6_bus_device *adev = isys->adev; + struct ipu7_fw_com_context *fwctx = isys->fwctx; + + if (!fwctx) + return; + + ipu6_ipu7_release_boot_config(adev); + + if (fwctx->fw_config) { + ipu6_dma_free(adev, sizeof(*fwctx->fw_config), fwctx->fw_config, + fwctx->fw_config_dma_addr, 0); + fwctx->fw_config = NULL; + fwctx->fw_config_dma_addr = 0; + } + + isys->fwctx = NULL; +} + +static int ipu7_fw_isys_open(struct ipu6_isys *isys) +{ + return ipu6_ipu7_boot_start_fw(isys->adev); +} + +static int ipu7_fw_isys_close(struct ipu6_isys *isys) +{ + int ret; + + ret = ipu6_ipu7_boot_stop_fw(isys->adev); + if (ret) + return ret; + + ipu7_fw_isys_cleanup(isys); + + return ret; +} + +static int ipu7_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams) +{ + struct ipu7_fw_com_queue_config *queue_configs; + struct ipu6_bus_device *adev = isys->adev; + struct device *dev = &adev->auxdev.dev; + struct ipu7_insys_config *fw_config; + struct ipu7_fw_com_context *fwctx; + dma_addr_t fw_config_dma_addr; + unsigned int num_queues; + u32 freq; + int ret; + + /* Allocate and init firmware context. */ + fwctx = devm_kzalloc(dev, sizeof(struct ipu7_fw_com_context), + GFP_KERNEL); + if (!fwctx) + return -ENOMEM; + + fwctx->num_input_queues = IPU7_INSYS_MAX_INPUT_QUEUES; + fwctx->num_output_queues = IPU7_INSYS_MAX_OUTPUT_QUEUES; + num_queues = fwctx->num_input_queues + fwctx->num_output_queues; + + queue_configs = devm_kcalloc(dev, num_queues, sizeof(*queue_configs), + GFP_KERNEL); + if (!queue_configs) { + ipu7_fw_isys_cleanup(isys); + return -ENOMEM; + } + fwctx->fw_entry = adev->fw_entry; + fwctx->queue_configs = queue_configs; + queue_configs[IPU7_INSYS_OUTPUT_MSG_QUEUE].max_capacity = + IPU7_ISYS_SIZE_RECV_QUEUE; + queue_configs[IPU7_INSYS_OUTPUT_MSG_QUEUE].token_size_in_bytes = + sizeof(struct ipu7_insys_resp); + queue_configs[IPU7_INSYS_OUTPUT_LOG_QUEUE].max_capacity = + IPU7_ISYS_SIZE_LOG_QUEUE; + queue_configs[IPU7_INSYS_OUTPUT_LOG_QUEUE].token_size_in_bytes = + sizeof(struct ipu7_insys_resp); + queue_configs[IPU7_INSYS_OUTPUT_RESERVED_QUEUE].max_capacity = 0; + queue_configs[IPU7_INSYS_OUTPUT_RESERVED_QUEUE].token_size_in_bytes = 0; + + queue_configs[IPU7_INSYS_INPUT_DEV_QUEUE].max_capacity = + IPU7_ISYS_MAX_STREAMS; + queue_configs[IPU7_INSYS_INPUT_DEV_QUEUE].token_size_in_bytes = + sizeof(struct ipu7_insys_send_queue_token); + + for (unsigned int i = IPU7_INSYS_INPUT_MSG_QUEUE; i < num_queues; i++) { + queue_configs[i].max_capacity = IPU7_ISYS_SIZE_SEND_QUEUE; + queue_configs[i].token_size_in_bytes = + sizeof(struct ipu7_insys_send_queue_token); + } + + /* Allocate ISYS subsys config. */ + fw_config = ipu6_dma_alloc(adev, sizeof(*fw_config), + &fw_config_dma_addr, GFP_KERNEL, 0); + if (!fw_config) { + dev_err(dev, "Failed to allocate isys subsys config.\n"); + ipu7_fw_isys_cleanup(isys); + return -ENOMEM; + } + fwctx->fw_config = fw_config; + fwctx->fw_config_dma_addr = fw_config_dma_addr; + memset(fw_config, 0, sizeof(*fw_config)); + fw_config->logger_config.use_source_severity = 0; + fw_config->logger_config.use_channels_enable_bitmask = 1; + fw_config->logger_config.channels_enable_bitmask = + IPU7_LOGGER_CFG_CHANNEL_ENABLE_SYSCOM; + fw_config->logger_config.hw_printf_buffer_base_addr = 0U; + fw_config->logger_config.hw_printf_buffer_size_bytes = 0U; + fw_config->wdt_config.wdt_timer1_us = 0; + fw_config->wdt_config.wdt_timer2_us = 0; + freq = ipu7_buttress_get_isys_freq(adev->isp); + + ipu6_dma_sync_single(adev, fw_config_dma_addr, + sizeof(struct ipu7_insys_config)); + + isys->fwctx = fwctx; + + ret = ipu6_ipu7_init_boot_config(adev, queue_configs, num_queues, + freq, fw_config_dma_addr, 1U); + if (ret) { + ipu7_fw_isys_cleanup(isys); + return ret; + } + + ret = ipu7_fw_isys_open(isys); + if (ret) + ipu7_fw_isys_cleanup(isys); + + return ret; +} + +static struct ipu7_insys_resp *ipu7_fw_isys_get_resp(struct ipu6_isys *isys) +{ + return ipu7_fw_com_get_token(isys->fwctx, IPU7_INSYS_OUTPUT_MSG_QUEUE); +} + +static void ipu7_fw_isys_put_resp(struct ipu6_isys *isys) +{ + ipu7_fw_com_put_token(isys->fwctx, IPU7_INSYS_OUTPUT_MSG_QUEUE); +} + +static int ipu7_isys_fw_pin_cfg(struct ipu6_isys_video *av, + struct ipu6_isys_stream *stream, + struct media_pad *src_pad, + struct v4l2_mbus_frame_desc_entry *entry, + void *__cfg) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity); + struct v4l2_subdev_state *state = v4l2_subdev_get_locked_active_state(sd); + struct ipu7_fw_isys_stream_cfg *cfg = __cfg; + struct ipu7_fw_isys_input_pin *input_pin; + struct ipu7_fw_isys_output_pin *output_pin; + struct ipu6_isys_queue *aq = &av->aq; + struct v4l2_mbus_framefmt *fmt; + const struct ipu6_isys_pixelformat *pfmt = + ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0); + int input_pins = cfg->nof_input_pins++; + int output_pins; + u32 src_stream; + + src_stream = __ipu6_isys_get_src_stream_by_src_pad(state, src_pad->index); + fmt = v4l2_subdev_state_get_format(state, src_pad->index, src_stream); + + input_pin = &cfg->input_pins[input_pins]; + input_pin->input_res.width = fmt->width; + input_pin->input_res.height = fmt->height; + input_pin->dt = entry->bus.csi2.dt; + input_pin->disable_mipi_unpacking = 0; + if (pfmt->bpp == pfmt->bpp_packed && pfmt->bpp % BITS_PER_BYTE) + input_pin->disable_mipi_unpacking = 1; + input_pin->mapped_dt = IPU7_N_INSYS_MIPI_DATA_TYPE; + input_pin->dt_rename_mode = IPU7_INSYS_MIPI_DT_NO_RENAME; + input_pin->sync_msg_map = + IPU7_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF | + IPU7_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED | + IPU7_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF | + IPU7_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED; + + output_pins = cfg->nof_output_pins++; + aq->fw_output = output_pins; + av->stream->output_pins_queue[output_pins] = aq; + + output_pin = &cfg->output_pins[output_pins]; + memset(output_pin, 0, sizeof(*output_pin)); + output_pin->link.buffer_lines = 0; + output_pin->link.foreign_key = IPU7_MSG_LINK_FOREIGN_KEY_NONE; + output_pin->link.pbk_id = IPU7_MSG_LINK_PBK_ID_DONT_CARE; + output_pin->link.pbk_slot_id = IPU7_MSG_LINK_PBK_SLOT_ID_DONT_CARE; + output_pin->link.dest = 0; /* IPU_INSYS_OUTPUT_LINK_DEST_MEM */ + output_pin->link.use_sw_managed = 1; + output_pin->crop.line_top = 0; + output_pin->crop.line_bottom = 0; + output_pin->dpcm.enable = 0; + output_pin->ft = pfmt->css_pixelformat; + output_pin->stride = ipu6_isys_get_bytes_per_line(av); + output_pin->send_irq = 1; + output_pin->input_pin_id = input_pins; + + return 0; +} + +static int +ipu7_fw_isys_send_cmd(struct ipu6_isys *isys, const unsigned int stream_handle, + void *cpu_mapped_buf, dma_addr_t dma_mapped_buf, + size_t size, u16 send_type) +{ + struct ipu7_fw_com_context *ctx = isys->fwctx; + /*struct device *dev = &isys->adev->auxdev.dev;*/ + struct ipu7_insys_send_queue_token *token; + + if (send_type >= N_IPU7_INSYS_SEND_TYPE) + return -EINVAL; + + if (cpu_mapped_buf) + clflush_cache_range(cpu_mapped_buf, size); + + token = ipu7_fw_com_get_token(ctx, stream_handle + + IPU7_INSYS_INPUT_MSG_QUEUE); + if (!token) + return -EBUSY; + + token->addr = dma_mapped_buf; + token->buf_handle = (unsigned long)cpu_mapped_buf; + token->send_type = send_type; + token->stream_id = stream_handle; + token->flag = IPU7_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE; + + ipu7_fw_com_put_token(ctx, stream_handle + IPU7_INSYS_INPUT_MSG_QUEUE); + /* now wakeup FW */ + ipu7_buttress_wakeup_isys(isys->adev->isp); + + return 0; +} + +static void ipu7_fw_isys_dump_stream_cfg(struct device *dev, + struct isys_fw_msgs *msg) +{ + struct ipu7_fw_isys_stream_cfg *cfg = &msg->ipu7.stream; + unsigned int i; + + dev_dbg(dev, "---------------------------\n"); + dev_dbg(dev, "IPU_FW_ISYS_STREAM_CFG_DATA\n"); + + dev_dbg(dev, ".port id %d\n", cfg->port_id); + dev_dbg(dev, ".vc %d\n", cfg->vc); + dev_dbg(dev, ".nof_input_pins = %d\n", cfg->nof_input_pins); + dev_dbg(dev, ".nof_output_pins = %d\n", cfg->nof_output_pins); + dev_dbg(dev, ".stream_msg_map = 0x%x\n", cfg->stream_msg_map); + + for (i = 0; i < cfg->nof_input_pins; i++) { + dev_dbg(dev, ".input_pin[%d]:\n", i); + dev_dbg(dev, "\t.dt = 0x%0x\n", + cfg->input_pins[i].dt); + dev_dbg(dev, "\t.disable_mipi_unpacking = %d\n", + cfg->input_pins[i].disable_mipi_unpacking); + dev_dbg(dev, "\t.dt_rename_mode = %d\n", + cfg->input_pins[i].dt_rename_mode); + dev_dbg(dev, "\t.mapped_dt = 0x%0x\n", + cfg->input_pins[i].mapped_dt); + dev_dbg(dev, "\t.input_res = %d x %d\n", + cfg->input_pins[i].input_res.width, + cfg->input_pins[i].input_res.height); + dev_dbg(dev, "\t.sync_msg_map = 0x%x\n", + cfg->input_pins[i].sync_msg_map); + } + + for (i = 0; i < cfg->nof_output_pins; i++) { + dev_dbg(dev, ".output_pin[%d]:\n", i); + dev_dbg(dev, "\t.input_pin_id = %d\n", + cfg->output_pins[i].input_pin_id); + dev_dbg(dev, "\t.stride = %d\n", cfg->output_pins[i].stride); + dev_dbg(dev, "\t.send_irq = %d\n", + cfg->output_pins[i].send_irq); + dev_dbg(dev, "\t.ft = %d\n", cfg->output_pins[i].ft); + + dev_dbg(dev, "\t.link.buffer_lines = %d\n", + cfg->output_pins[i].link.buffer_lines); + dev_dbg(dev, "\t.link.foreign_key = %d\n", + cfg->output_pins[i].link.foreign_key); + dev_dbg(dev, "\t.link.granularity_pointer_update = %d\n", + cfg->output_pins[i].link.granularity_pointer_update); + dev_dbg(dev, "\t.link.msg_link_streaming_mode = %d\n", + cfg->output_pins[i].link.msg_link_streaming_mode); + dev_dbg(dev, "\t.link.pbk_id = %d\n", + cfg->output_pins[i].link.pbk_id); + dev_dbg(dev, "\t.link.pbk_slot_id = %d\n", + cfg->output_pins[i].link.pbk_slot_id); + dev_dbg(dev, "\t.link.dest = %d\n", + cfg->output_pins[i].link.dest); + dev_dbg(dev, "\t.link.use_sw_managed = %d\n", + cfg->output_pins[i].link.use_sw_managed); + dev_dbg(dev, "\t.link.is_snoop = %d\n", + cfg->output_pins[i].link.is_snoop); + + dev_dbg(dev, "\t.crop.line_top = %d\n", + cfg->output_pins[i].crop.line_top); + dev_dbg(dev, "\t.crop.line_bottom = %d\n", + cfg->output_pins[i].crop.line_bottom); + + dev_dbg(dev, "\t.dpcm_enable = %d\n", + cfg->output_pins[i].dpcm.enable); + dev_dbg(dev, "\t.dpcm.type = %d\n", + cfg->output_pins[i].dpcm.type); + dev_dbg(dev, "\t.dpcm.predictor = %d\n", + cfg->output_pins[i].dpcm.predictor); + } + dev_dbg(dev, "---------------------------\n"); +} + +static void ipu7_fw_isys_dump_frame_buf_set(struct device *dev, + struct isys_fw_msgs *msg, + unsigned int outputs) +{ + struct ipu7_fw_isys_frame_buff_set *buf = &msg->ipu7.frame; + + dev_dbg(dev, "--------------------------\n"); + dev_dbg(dev, "IPU_ISYS_BUFF_SET\n"); + dev_dbg(dev, ".capture_msg_map = %d\n", buf->capture_msg_map); + dev_dbg(dev, ".frame_id = %d\n", buf->frame_id); + dev_dbg(dev, ".skip_frame = %d\n", buf->skip_frame); + + for (unsigned int i = 0; i < outputs; i++) { + dev_dbg(dev, ".output_pin[%d]:\n", i); + dev_dbg(dev, "\t.user_token = %llx\n", + buf->output_pins[i].user_token); + dev_dbg(dev, "\t.addr = 0x%x\n", buf->output_pins[i].addr); + } + dev_dbg(dev, "---------------------------\n"); +} + +static int ipu7_fw_isys_prepare_stream_cfg(struct ipu6_isys_stream *stream, + struct v4l2_mbus_frame_desc *desc, + struct isys_fw_msgs *msg) +{ + struct ipu7_fw_isys_stream_cfg *cfg = &msg->ipu7.stream; + struct device *dev = &stream->isys->adev->auxdev.dev; + int ret; + + memset(cfg, 0, sizeof(*cfg)); + cfg->port_id = stream->asd->source; + cfg->vc = stream->vc; + cfg->stream_msg_map = IPU7_INSYS_STREAM_ENABLE_MSG_SEND_RESP | + IPU7_INSYS_STREAM_ENABLE_MSG_SEND_IRQ; + + ret = ipu6_isys_fw_pins_prepare(stream, desc, ipu7_isys_fw_pin_cfg, + cfg); + if (ret) + return ret; + + stream->nr_output_pins = cfg->nof_output_pins; + + ipu7_fw_isys_dump_stream_cfg(dev, msg); + + return 0; +} + +static void +ipu7_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb, + struct ipu7_fw_isys_frame_buff_set *set) +{ + struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); + struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); + struct ipu6_isys_video_buffer *ivb = + vb2_buffer_to_ipu6_isys_video_buffer(vvb); + + set->output_pins[aq->fw_output].addr = ivb->dma_addr; + set->output_pins[aq->fw_output].user_token = (u64)(uintptr_t)set; +} + +static void +ipu7_fw_isys_prepare_buf_set(struct isys_fw_msgs *msg, + struct ipu6_isys_stream *stream, + struct ipu6_isys_buffer_list *bl) +{ + struct ipu7_fw_isys_frame_buff_set *set = &msg->ipu7.frame; + struct ipu6_isys_buffer *ib; + + WARN_ON(!bl->nbufs); + + memset(set, 0, sizeof(*set)); + set->capture_msg_map = IPU7_INSYS_FRAME_ENABLE_MSG_SEND_RESP | + IPU7_INSYS_FRAME_ENABLE_MSG_SEND_IRQ; + set->frame_id = atomic_fetch_inc(&stream->buf_id) % 256; + + list_for_each_entry(ib, &bl->head, head) { + struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib); + + ipu7_isys_buf_to_fw_frame_buf_pin(vb, set); + } + + dev_dbg(&stream->isys->adev->auxdev.dev, + "ipu7 frame_buf: cap_map=0x%x fid=%u opin[0].addr=0x%x token=0x%llx\n", + set->capture_msg_map, set->frame_id, + set->output_pins[0].addr, set->output_pins[0].user_token); +} + +static int ipu7_fw_isys_stream_open(struct ipu6_isys *isys, + const unsigned int stream_handle, + struct isys_fw_msgs *msg) +{ + return ipu7_fw_isys_send_cmd(isys, stream_handle, &msg->ipu7.stream, + msg->dma_addr, sizeof(msg->ipu7.stream), + IPU7_INSYS_SEND_TYPE_STREAM_OPEN); +} + +static int ipu7_fw_isys_stream_close(struct ipu6_isys *isys, + const unsigned int stream_handle) +{ + return ipu7_fw_isys_send_cmd(isys, stream_handle, NULL, 0, 0, + IPU7_INSYS_SEND_TYPE_STREAM_CLOSE); +} + +static int ipu7_fw_isys_stream_flush(struct ipu6_isys *isys, + const unsigned int stream_handle) +{ + return ipu7_fw_isys_send_cmd(isys, stream_handle, NULL, 0, 0, + IPU7_INSYS_SEND_TYPE_STREAM_FLUSH); +} + +static int ipu7_fw_isys_stream_start(struct ipu6_isys *isys, + const unsigned int stream_handle, + struct isys_fw_msgs *msg, bool capture) +{ + return ipu7_fw_isys_send_cmd(isys, stream_handle, &msg->ipu7.frame, + msg->dma_addr, sizeof(msg->ipu7.frame), + IPU7_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE); +} + +static int ipu7_fw_isys_stream_capture(struct ipu6_isys *isys, + const unsigned int stream_handle, + struct isys_fw_msgs *msg) +{ + return ipu7_fw_isys_send_cmd(isys, stream_handle, &msg->ipu7.frame, + msg->dma_addr, sizeof(msg->ipu7.frame), + IPU7_INSYS_SEND_TYPE_STREAM_CAPTURE); +} + +const struct ipu6_fw_isys_ops ipu7_fw_isys_ops = { + .init = ipu7_fw_isys_init, + .close = ipu7_fw_isys_close, + .send_cmd = ipu7_fw_isys_send_cmd, + .cleanup = ipu7_fw_isys_cleanup, + .prepare_stream_cfg = ipu7_fw_isys_prepare_stream_cfg, + .prepare_buf_set = ipu7_fw_isys_prepare_buf_set, + .stream_open = ipu7_fw_isys_stream_open, + .stream_start = ipu7_fw_isys_stream_start, + .stream_capture = ipu7_fw_isys_stream_capture, + .stream_flush = ipu7_fw_isys_stream_flush, + .stream_close = ipu7_fw_isys_stream_close, + .dump_stream_cfg = ipu7_fw_isys_dump_stream_cfg, + .dump_frame_buf_set = ipu7_fw_isys_dump_frame_buf_set, +}; + +static const struct ipu7_csi2_error { + const char *error_string; + bool is_info_only; +} dphy_rx_errors[] = { + { "Error handler FIFO full", false }, + { "Reserved Short Packet encoding detected", true }, + { "Reserved Long Packet encoding detected", true }, + { "Received packet is too short", false}, + { "Received packet is too long", false}, + { "Short packet discarded due to errors", false }, + { "Long packet discarded due to errors", false }, + { "CSI Combo Rx interrupt", false }, + { "IDI CDC FIFO overflow(remaining bits are reserved as 0)", false }, + { "Received NULL packet", true }, + { "Received blanking packet", true }, + { "Tie to 0", true }, +}; + +static void ipu7_isys_register_errors(struct ipu6_isys_csi2 *csi2) +{ + u32 offset = IPU7_IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port); + u32 status = readl(csi2->base + offset + IPU7_IRQ_CTL_STATUS); + u32 mask = IPU7_CSI_RX_ERROR_IRQ_MASK; + + if (!status) + return; + + dev_dbg(&csi2->isys->adev->auxdev.dev, "csi2-%u error status 0x%08x\n", + csi2->port, status); + + writel(status & mask, csi2->base + offset + IPU7_IRQ_CTL_CLEAR); + csi2->receiver_errors |= status & mask; +} + +static void ipu7_isys_csi2_error(struct ipu6_isys_csi2 *csi2) +{ + u32 status; + + /* Register errors once more in case of error interrupts are disabled */ + ipu7_isys_register_errors(csi2); + status = csi2->receiver_errors; + csi2->receiver_errors = 0; + + for (unsigned int i = 0; i < ARRAY_SIZE(dphy_rx_errors); i++) { + if (status & BIT(i)) + dev_err_ratelimited(&csi2->isys->adev->auxdev.dev, + "csi2-%i error: %s\n", + csi2->port, + dphy_rx_errors[i].error_string); + } +} + +static const struct resp_to_msg { + enum ipu7_insys_resp_type type; + const char *msg; +} is_fw_msg[] = { + { IPU7_INSYS_RESP_TYPE_STREAM_OPEN_DONE, "STREAM_OPEN_DONE" }, + { IPU7_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK, + "STREAM_START_AND_CAPTURE_ACK" }, + { IPU7_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK, "STREAM_CAPTURE_ACK" }, + { IPU7_INSYS_RESP_TYPE_STREAM_ABORT_ACK, "STREAM_ABORT_ACK" }, + { IPU7_INSYS_RESP_TYPE_STREAM_FLUSH_ACK, "STREAM_FLUSH_ACK" }, + { IPU7_INSYS_RESP_TYPE_STREAM_CLOSE_ACK, "STREAM_CLOSE_ACK" }, + { IPU7_INSYS_RESP_TYPE_PIN_DATA_READY, "PIN_DATA_READY" }, + { IPU7_INSYS_RESP_TYPE_FRAME_SOF, "FRAME_SOF" }, + { IPU7_INSYS_RESP_TYPE_FRAME_EOF, "FRAME_EOF" }, + { IPU7_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE, + "STREAM_START_AND_CAPTURE_DONE" }, + { IPU7_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE, "STREAM_CAPTURE_DONE" }, + { N_IPU7_INSYS_RESP_TYPE, "N_IPU7_INSYS_RESP_TYPE" }, +}; + +static int ipu7_isys_isr_one(struct ipu6_bus_device *adev) +{ + struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); + struct ipu6_isys_stream *stream = NULL; + struct device *dev = &adev->auxdev.dev; + struct ipu6_isys_csi2 *csi2 = NULL; + struct ipu7_fw_isys_msg_err err_info; + struct isys_fw_msgs *isys_fw_msg; + struct ipu7_insys_resp *resp; + unsigned long flags; + u64 ts; + + resp = ipu7_fw_isys_get_resp(isys); + if (!resp) + return 1; + + if (resp->type >= N_IPU7_INSYS_RESP_TYPE) { + dev_err(dev, "Unknown response type %u stream %u\n", + resp->type, resp->stream_id); + ipu7_fw_isys_put_resp(isys); + return 1; + } + + err_info = resp->error_info; + ts = ((u64)resp->timestamp[1] << 32) | resp->timestamp[0]; + + if (err_info.err_group == INSYS_MSG_ERR_GROUP_CAPTURE && + err_info.err_code == INSYS_MSG_ERR_CAPTURE_SYNC_FRAME_DROP) { + /* receive a sp w/o command, firmware drop it */ + dev_dbg(dev, "FRAME DROP: %02u %s stream %u\n", + resp->type, is_fw_msg[resp->type].msg, + resp->stream_id); + dev_dbg(dev, "\tpin %u buf_id %llx frame %u\n", + resp->pin_id, resp->buf_id, resp->frame_id); + dev_dbg(dev, "\terror group %u code %u details [%u %u]\n", + err_info.err_group, err_info.err_code, + err_info.err_detail[0], err_info.err_detail[1]); + } else if (err_info.err_code) { + dev_err(dev, "%02u %s stream %u pin %u buf_id %llx frame %u\n", + resp->type, is_fw_msg[resp->type].msg, resp->stream_id, + resp->pin_id, resp->buf_id, resp->frame_id); + dev_err(dev, "\terror group %u code %u details [%u %u]\n", + err_info.err_group, err_info.err_code, + err_info.err_detail[0], err_info.err_detail[1]); + } else { + dev_dbg(dev, "%02u %s stream %u pin %u buf_id %llx frame %u\n", + resp->type, is_fw_msg[resp->type].msg, resp->stream_id, + resp->pin_id, resp->buf_id, resp->frame_id); + dev_dbg(dev, "\tts %llu\n", ts); + } + + if (resp->stream_id >= IPU7_ISYS_MAX_STREAMS) { + dev_err(dev, "bad stream handle %u\n", + resp->stream_id); + goto leave_nounlock; + } + + spin_lock_irqsave(&isys->streams_lock, flags); + + stream = resp->stream_id < IPU6_ISYS_MAX_STREAMS ? + isys->streams_by_handle[resp->stream_id] : NULL; + if (!stream) { + dev_err(dev, "stream of stream_handle %u is unused\n", + resp->stream_id); + goto leave; + } + + stream->error = err_info.err_code; + + if (stream->asd) + csi2 = ipu6_isys_subdev_to_csi2(stream->asd); + + switch (resp->type) { + case IPU7_INSYS_RESP_TYPE_STREAM_OPEN_DONE: + complete(&stream->stream_open_completion); + break; + case IPU7_INSYS_RESP_TYPE_STREAM_CLOSE_ACK: + complete(&stream->stream_close_completion); + break; + case IPU7_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK: + complete(&stream->stream_start_completion); + break; + case IPU7_INSYS_RESP_TYPE_STREAM_ABORT_ACK: + complete(&stream->stream_stop_completion); + break; + case IPU7_INSYS_RESP_TYPE_STREAM_FLUSH_ACK: + complete(&stream->stream_stop_completion); + break; + case IPU7_INSYS_RESP_TYPE_PIN_DATA_READY: + /* + * firmware only release the capture msg until software + * get pin_data_ready event + */ + isys_fw_msg = container_of((void *)(uintptr_t)resp->buf_id, + struct isys_fw_msgs, dummy); + + ipu6_put_fw_msg_buf(ipu6_bus_get_drvdata(adev), isys_fw_msg); + if (resp->pin_id < IPU6_ISYS_OUTPUT_PINS) + ipu6_stream_buf_ready(stream, resp->pin_id, + resp->pin.addr, ts, 0); + else + dev_err(dev, "No handler for pin %u ready\n", + resp->pin_id); + if (csi2) + ipu7_isys_csi2_error(csi2); + + break; + case IPU7_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK: + break; + case IPU7_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE: + case IPU7_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE: + break; + case IPU7_INSYS_RESP_TYPE_FRAME_SOF: + if (csi2) + ipu6_isys_csi2_sof_event_by_stream(stream); + + stream->seq[stream->seq_index].sequence = + atomic_read(&stream->sequence) - 1U; + stream->seq[stream->seq_index].timestamp = ts; + dev_dbg(dev, + "SOF: stream %u frame %u (index %u), ts 0x%16.16llx\n", + resp->stream_id, resp->frame_id, + stream->seq[stream->seq_index].sequence, ts); + stream->seq_index = (stream->seq_index + 1U) + % IPU6_ISYS_MAX_PARALLEL_SOF; + break; + case IPU7_INSYS_RESP_TYPE_FRAME_EOF: + if (csi2) + ipu6_isys_csi2_eof_event_by_stream(stream); + + dev_dbg(dev, "eof: stream %d(index %u) ts 0x%16.16llx\n", + resp->stream_id, + stream->seq[stream->seq_index].sequence, ts); + break; + default: + dev_err(dev, "Unknown response type %u stream %u\n", + resp->type, resp->stream_id); + break; + } + +leave: + spin_unlock_irqrestore(&isys->streams_lock, flags); + +leave_nounlock: + ipu7_fw_isys_put_resp(isys); + + return 0; +} + +#define IPU7_NR_OF_CSI2_VC 16U +static void ipu7_isys_csi2_isr(struct ipu6_isys_csi2 *csi2) +{ + struct device *dev = &csi2->isys->adev->auxdev.dev; + struct ipu6_device *isp = csi2->isys->adev->isp; + struct ipu6_isys_stream *s; + u32 sync, offset; + u32 fe = 0; + u8 vc; + + ipu7_isys_register_errors(csi2); + + offset = IPU7_IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port); + sync = readl(csi2->base + offset + IPU7_IRQ_CTL_STATUS); + writel(sync, csi2->base + offset + IPU7_IRQ_CTL_CLEAR); + dev_dbg(dev, "csi2-%u sync status 0x%08x\n", csi2->port, sync); + + if (!IS_IPU7_MTL(isp)) { + fe = readl(csi2->base + offset + IPU7_IRQ1_CTL_STATUS); + writel(fe, csi2->base + offset + IPU7_IRQ1_CTL_CLEAR); + dev_dbg(dev, "csi2-%u FE status 0x%08x\n", csi2->port, fe); + } + + for (vc = 0; vc < IPU7_NR_OF_CSI2_VC && (sync || fe); vc++) { + s = csi2->streams_by_vc[vc]; + if (!s) + continue; + + if (!IS_IPU7_MTL(isp)) { + if (sync & IPU7P5_CSI_RX_SYNC_FS_VC & (1U << vc)) + ipu6_isys_csi2_sof_event_by_stream(s); + + if (fe & IPU7P5_CSI_RX_SYNC_FE_VC & (1U << vc)) + ipu6_isys_csi2_eof_event_by_stream(s); + } else { + if (sync & IPU7_CSI_RX_SYNC_FS_VC & (1U << (vc * 2))) + ipu6_isys_csi2_sof_event_by_stream(s); + + if (sync & IPU7_CSI_RX_SYNC_FE_VC & (2U << (vc * 2))) + ipu6_isys_csi2_eof_event_by_stream(s); + } + } +} + +static void ipu7_dispatch_csi2_isr(struct ipu6_isys *isys, u32 status) +{ + for (unsigned int i = 0; i < isys->pdata->ipdata->csi2.nports; i++) { + if (!isys->csi2[i].base) + continue; + if (status & isys->csi2[i].legacy_irq_mask) + ipu7_isys_csi2_isr(&isys->csi2[i]); + } +} + +irqreturn_t ipu7_isys_isr(struct ipu6_bus_device *adev) +{ + struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); + void __iomem *base = isys->pdata->base; + u32 status_sw, status_csi; + u32 csi_offset, sw_offset; + int pm_status; + + pm_status = pm_runtime_get_if_active(&adev->auxdev.dev); + if (!pm_status) + return 0; + + csi_offset = IPU7_IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE; + sw_offset = IPU7_IS_UC_CTRL_BASE; + + status_csi = readl(base + csi_offset + IPU7_IRQ_CTL_STATUS); + status_sw = readl(base + sw_offset + IPU7_TO_SW_IRQ_CNTL_STATUS); + + if (!status_csi && !status_sw) { + if (pm_status > 0) + pm_runtime_put(&adev->auxdev.dev); + return IRQ_NONE; + } + + do { + writel(status_sw, base + sw_offset + IPU7_TO_SW_IRQ_CNTL_CLEAR); + writel(status_csi, base + csi_offset + IPU7_IRQ_CTL_CLEAR); + + if (isys->isr_csi2_bits & status_csi) + ipu7_dispatch_csi2_isr(isys, status_csi); + + if (!ipu7_isys_isr_one(adev)) + status_sw = IPU7_TO_SW_IRQ_FW; + else + status_sw = 0; + + status_csi = readl(base + csi_offset + IPU7_IRQ_CTL_STATUS); + status_sw |= readl(base + sw_offset + + IPU7_TO_SW_IRQ_CNTL_STATUS); + } while ((status_csi & isys->isr_csi2_bits) || + (status_sw & IPU7_TO_SW_IRQ_FW)); + + writel(IPU7_IS_UC_TO_SW_IRQ_MASK, + base + sw_offset + IPU7_TO_SW_IRQ_CNTL_MASK_N); + + if (pm_status > 0) + pm_runtime_put(&adev->auxdev.dev); + + return IRQ_HANDLED; +} diff --git a/drivers/media/pci/intel/ipu6/ipu7-fw-isys.h b/drivers/media/pci/intel/ipu6/ipu7-fw-isys.h new file mode 100644 index 000000000000..d5289f6add8c --- /dev/null +++ b/drivers/media/pci/intel/ipu6/ipu7-fw-isys.h @@ -0,0 +1,296 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2026 Intel Corporation */ + +#ifndef IPU7_FW_ISYS_H +#define IPU7_FW_ISYS_H + +#define IPU7_FWLOG_MAX_LOGGER_SOURCES (64U) +#define IPU7_INSYS_MAX_OUTPUT_QUEUES 3U +#define IPU7_INSYS_STREAM_ID_MAX 16U +#define IPU7_INSYS_MAX_INPUT_QUEUES (IPU7_INSYS_STREAM_ID_MAX + 1U) +#define IPU7_INSYS_OUTPUT_MSG_QUEUE 0U +#define IPU7_INSYS_OUTPUT_LOG_QUEUE 1U +#define IPU7_INSYS_OUTPUT_RESERVED_QUEUE 2U +#define IPU7_INSYS_INPUT_DEV_QUEUE 3U + +#define IPU7_INSYS_INPUT_FIRST_QUEUE 3U +#define IPU7_INSYS_INPUT_MSG_QUEUE 4U +#define IPU7_INSYS_INPUT_MSG_MAX_QUEUE 16U + +#define IPU7_MSG_ERR_MAX_DETAILS 4U +#define IPU7_ISYS_SIZE_RECV_QUEUE 40U +#define IPU7_ISYS_SIZE_LOG_QUEUE 256U +#define IPU7_ISYS_SIZE_SEND_QUEUE 40U +#define IPU7_ISYS_NUM_RECV_QUEUE 1U +#define IPU7_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE 0U + +#define IPU7_LOGGER_CFG_CHANNEL_ENABLE_SYSCOM BIT(1) + +#define IPU7_ISYS_MAX_STREAMS 16U +#define IPU7_MAX_OPINS 4 +#define IPU7_MAX_IPINS 4 + +#define IPU7_N_INSYS_MIPI_DATA_TYPE 0x40 + +#define IPU7_MSG_LINK_FOREIGN_KEY_NONE (65535U) +#define IPU7_MSG_LINK_PBK_ID_DONT_CARE (255U) +#define IPU7_MSG_LINK_PBK_SLOT_ID_DONT_CARE (255U) + +#define IPU7_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF BIT(0) +#define IPU7_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF BIT(1) +#define IPU7_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF BIT(2) +#define IPU7_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF BIT(3) +#define IPU7_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED BIT(4) +#define IPU7_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF_DISCARDED BIT(5) +#define IPU7_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED BIT(6) +#define IPU7_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF_DISCARDED BIT(7) + +#define IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_OPEN_DONE BIT(0) +#define IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_OPEN_DONE BIT(1) +#define IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_START_ACK BIT(2) +#define IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_START_ACK BIT(3) +#define IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_CLOSE_ACK BIT(4) +#define IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_CLOSE_ACK BIT(5) +#define IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_FLUSH_ACK BIT(6) +#define IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_FLUSH_ACK BIT(7) +#define IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_ABORT_ACK BIT(8) +#define IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_ABORT_ACK BIT(9) + +#define IPU7_INSYS_STREAM_ENABLE_MSG_SEND_RESP ( \ + IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_OPEN_DONE | \ + IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_START_ACK | \ + IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_CLOSE_ACK | \ + IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_FLUSH_ACK | \ + IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_ABORT_ACK) +#define IPU7_INSYS_STREAM_ENABLE_MSG_SEND_IRQ ( \ + IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_OPEN_DONE | \ + IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_START_ACK | \ + IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_CLOSE_ACK | \ + IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_FLUSH_ACK | \ + IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_ABORT_ACK) + +#define IPU7_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_ACK BIT(0) +#define IPU7_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_ACK BIT(1) +#define IPU7_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_DONE BIT(2) +#define IPU7_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_DONE BIT(3) +#define IPU7_INSYS_FRAME_MSG_SEND_RESP_PIN_DATA_READY BIT(4) +#define IPU7_INSYS_FRAME_MSG_SEND_IRQ_PIN_DATA_READY BIT(5) + +#define IPU7_INSYS_FRAME_ENABLE_MSG_SEND_RESP ( \ + IPU7_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_ACK | \ + IPU7_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_DONE | \ + IPU7_INSYS_FRAME_MSG_SEND_RESP_PIN_DATA_READY) +#define IPU7_INSYS_FRAME_ENABLE_MSG_SEND_IRQ ( \ + IPU7_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_ACK | \ + IPU7_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_DONE | \ + IPU7_INSYS_FRAME_MSG_SEND_IRQ_PIN_DATA_READY) + +enum ipu7_insys_send_type { + IPU7_INSYS_SEND_TYPE_STREAM_OPEN = 0, + IPU7_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE = 1, + IPU7_INSYS_SEND_TYPE_STREAM_CAPTURE = 2, + IPU7_INSYS_SEND_TYPE_STREAM_ABORT = 3, + IPU7_INSYS_SEND_TYPE_STREAM_FLUSH = 4, + IPU7_INSYS_SEND_TYPE_STREAM_CLOSE = 5, + N_IPU7_INSYS_SEND_TYPE +}; + +enum ipu7_insys_resp_type { + IPU7_INSYS_RESP_TYPE_STREAM_OPEN_DONE = 0, + IPU7_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK = 1, + IPU7_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK = 2, + IPU7_INSYS_RESP_TYPE_STREAM_ABORT_ACK = 3, + IPU7_INSYS_RESP_TYPE_STREAM_FLUSH_ACK = 4, + IPU7_INSYS_RESP_TYPE_STREAM_CLOSE_ACK = 5, + IPU7_INSYS_RESP_TYPE_PIN_DATA_READY = 6, + IPU7_INSYS_RESP_TYPE_FRAME_SOF = 7, + IPU7_INSYS_RESP_TYPE_FRAME_EOF = 8, + IPU7_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE = 9, + IPU7_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE = 10, + IPU7_INSYS_RESP_TYPE_PWM_IRQ = 11, + N_IPU7_INSYS_RESP_TYPE +}; + +enum ipu7_insys_mipi_dt_rename_mode { + IPU7_INSYS_MIPI_DT_NO_RENAME = 0, + IPU7_INSYS_MIPI_DT_RENAMED_MODE = 1, + N_IPU7_INSYS_MIPI_DT_MODE +}; + +enum insys_msg_err_capture { + INSYS_MSG_ERR_CAPTURE_OK = 0, + INSYS_MSG_ERR_CAPTURE_STREAM_ID = 1, + INSYS_MSG_ERR_CAPTURE_PAYLOAD_PTR = 2, + INSYS_MSG_ERR_CAPTURE_MEM_SLOT = 3, + INSYS_MSG_ERR_CAPTURE_STREAMING_MODE = 4, + INSYS_MSG_ERR_CAPTURE_AVAILABLE_CMD_SLOT = 5, + INSYS_MSG_ERR_CAPTURE_CONSUMED_CMD_SLOT = 6, + INSYS_MSG_ERR_CAPTURE_CMD_SLOT_PAYLOAD_PTR = 7, + INSYS_MSG_ERR_CAPTURE_CMD_PREPARE = 8, + INSYS_MSG_ERR_CAPTURE_OUTPUT_PIN = 9, + INSYS_MSG_ERR_CAPTURE_SYNC_FRAME_DROP = 10, + INSYS_MSG_ERR_CAPTURE_FRAME_MESSAGES_MAP = 11, + INSYS_MSG_ERR_CAPTURE_TIMEOUT = 12, + INSYS_MSG_ERR_CAPTURE_INVALID_STREAM_STATE = 13, + INSYS_MSG_ERR_CAPTURE_HW_ERR_MULTIBIT_PH_ERROR_DETECTED = 14, + INSYS_MSG_ERR_CAPTURE_HW_ERR_PAYLOAD_CRC_ERROR = 15, + INSYS_MSG_ERR_CAPTURE_HW_ERR_INPUT_DATA_LOSS_ELASTIC_FIFO_OVFL = 16, + INSYS_MSG_ERR_CAPTURE_HW_ERR_PIXEL_BUFFER_OVERFLOW = 17, + INSYS_MSG_ERR_CAPTURE_HW_ERR_BAD_FRAME_DIM = 18, + INSYS_MSG_ERR_CAPTURE_HW_ERR_PHY_SYNC_ERR = 19, + INSYS_MSG_ERR_CAPTURE_HW_ERR_SECURE_TOUCH = 20, + INSYS_MSG_ERR_CAPTURE_HW_ERR_MASTER_SLAVE_SYNC_ERR = 21, + INSYS_MSG_ERR_CAPTURE_FRAME_SKIP_ERR = 22, + INSYS_MSG_ERR_CAPTURE_FE_INPUT_FIFO_OVERFLOW_ERR = 23, + INSYS_MSG_ERR_CAPTURE_CMD_SUBMIT_TO_HW = 24, + INSYS_MSG_ERR_CAPTURE_N +}; + +enum insys_msg_err_groups { + INSYS_MSG_ERR_GROUP_RESERVED = 0, + INSYS_MSG_ERR_GROUP_GENERAL = 1, + INSYS_MSG_ERR_GROUP_STREAM = 2, + INSYS_MSG_ERR_GROUP_CAPTURE = 3, + INSYS_MSG_ERR_GROUP_N, +}; + +struct ipu7_fw_isys_logger_config { + u8 use_source_severity; + u8 source_severity[IPU7_FWLOG_MAX_LOGGER_SOURCES]; + u8 use_channels_enable_bitmask; + u8 channels_enable_bitmask; + u8 padding[1]; + u32 hw_printf_buffer_base_addr; + u32 hw_printf_buffer_size_bytes; +}; + +struct ipu7_wdt_abi { + u32 wdt_timer1_us; + u32 wdt_timer2_us; +}; + +struct ipu7_insys_config { + u32 timeout_val_ms; + struct ipu7_fw_isys_logger_config logger_config; + struct ipu7_wdt_abi wdt_config; +}; + +struct ipu7_insys_capture_output_pin_payload { + u64 user_token; + u32 addr; + u8 pad[4]; +}; + +struct ipu7_fw_isys_msg_err { + u32 err_group; + u32 err_code; + u32 err_detail[IPU7_MSG_ERR_MAX_DETAILS]; +}; + +struct ipu7_insys_resp { + u64 buf_id; + struct ipu7_insys_capture_output_pin_payload pin; + struct ipu7_fw_isys_msg_err error_info; + u32 timestamp[2]; + u8 type; + u8 msg_link_streaming_mode; + u8 stream_id; + u8 pin_id; + u8 frame_id; + u8 skip_frame; + u8 pad[2]; +}; + +struct ipu7_insys_resp_queue_token { + struct ipu7_insys_resp resp_info; +}; + +struct ipu7_insys_send_queue_token { + u64 buf_handle; + u32 addr; + u16 stream_id; + u8 send_type; + u8 flag; +}; + +struct ipu7_fw_isys_output_link { + u32 buffer_lines; + u16 foreign_key; + u16 granularity_pointer_update; + u8 msg_link_streaming_mode; + u8 pbk_id; + u8 pbk_slot_id; + u8 dest; + u8 use_sw_managed; + u8 is_snoop; + u8 pad[2]; +} __packed; + +struct ipu7_fw_isys_output_cropping { + u16 line_top; + u16 line_bottom; +} __packed; + +struct ipu7_fw_isys_output_dpcm { + u8 enable; + u8 type; + u8 predictor; + u8 pad; +} __packed; + +struct ipu7_fw_isys_output_pin { + struct ipu7_fw_isys_output_link link; + struct ipu7_fw_isys_output_cropping crop; + struct ipu7_fw_isys_output_dpcm dpcm; + u32 stride; + u16 ft; + u8 send_irq; + u8 input_pin_id; + u8 early_ack_en; + u8 pad[3]; +} __packed; + +struct ipu7_fw_isys_resolution { + u32 width; + u32 height; +} __packed; + +struct ipu7_fw_isys_input_pin { + struct ipu7_fw_isys_resolution input_res; + u16 sync_msg_map; + u8 dt; + u8 disable_mipi_unpacking; + u8 dt_rename_mode; + u8 mapped_dt; + u8 pad[2]; +} __packed; + +struct ipu7_fw_isys_stream_cfg { + struct ipu7_fw_isys_input_pin input_pins[IPU7_MAX_IPINS]; + struct ipu7_fw_isys_output_pin output_pins[IPU7_MAX_OPINS]; + u16 stream_msg_map; + u8 port_id; + u8 vc; + u8 nof_input_pins; + u8 nof_output_pins; + u8 pad[2]; +} __packed; + +struct ipu7_fw_isys_capture_output_pin { + u64 user_token; + u32 addr; + u8 pad[4]; +} __packed; + +struct ipu7_fw_isys_frame_buff_set { + struct ipu7_fw_isys_capture_output_pin output_pins[IPU7_MAX_OPINS]; + u8 capture_msg_map; + u8 frame_id; + u8 skip_frame; + u8 pad[5]; +} __packed; + +struct ipu6_fw_isys_ops *ipu7_fw_isys_get_ops(void); +irqreturn_t ipu7_isys_isr(struct ipu6_bus_device *adev); + +#endif diff --git a/drivers/media/pci/intel/ipu6/ipu7-isys-csi-phy.c b/drivers/media/pci/intel/ipu6/ipu7-isys-csi-phy.c new file mode 100644 index 000000000000..10273c687faa --- /dev/null +++ b/drivers/media/pci/intel/ipu6/ipu7-isys-csi-phy.c @@ -0,0 +1,1072 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 - 2026 Intel Corporation + */ + +#include <linux/bitmap.h> +#include <linux/bug.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/types.h> + +#include <media/mipi-csi2.h> +#include <media/v4l2-device.h> + +#include "ipu6.h" +#include "ipu6-bus.h" +#include "ipu6-isys.h" +#include "ipu6-isys-csi2.h" +#include "ipu7-isys-csi2-regs.h" +#include "ipu7-isys-csi-phy.h" + +#define PORT_A 0U +#define PORT_B 1U +#define PORT_C 2U +#define PORT_D 3U + +#define N_DATA_IDS 8U +static DECLARE_BITMAP(data_ids, N_DATA_IDS); + +struct ddlcal_counter_ref_s { + u16 min_mbps; + u16 max_mbps; + + u16 ddlcal_counter_ref; +}; + +struct ddlcal_params { + u16 min_mbps; + u16 max_mbps; + u16 oa_lanex_hsrx_cdphy_sel_fast; + u16 ddlcal_max_phase; + u16 phase_bound; + u16 ddlcal_dll_fbk; + u16 ddlcal_ddl_coarse_bank; + u16 fjump_deskew; + u16 min_eye_opening_deskew; +}; + +struct i_thssettle_params { + u16 min_mbps; + u16 max_mbps; + u16 i_thssettle; +}; + +/* lane2 for 4l3t, lane1 for 2l2t */ +struct oa_lane_clk_div_params { + u16 min_mbps; + u16 max_mbps; + u16 oa_lane_hsrx_hs_clk_div; +}; + +struct cdr_fbk_cap_prog_params { + u16 min_mbps; + u16 max_mbps; + u16 val; +}; + +static const struct ddlcal_counter_ref_s table0[] = { + { 1500, 1999, 118 }, + { 2000, 2499, 157 }, + { 2500, 3499, 196 }, + { 3500, 4499, 274 }, + { 4500, 4500, 352 }, + { } +}; + +static const struct ddlcal_params table1[] = { + { 1500, 1587, 0, 143, 167, 17, 3, 4, 29 }, + { 1588, 1687, 0, 135, 167, 15, 3, 4, 27 }, + { 1688, 1799, 0, 127, 135, 15, 2, 4, 26 }, + { 1800, 1928, 0, 119, 135, 13, 2, 3, 24 }, + { 1929, 2076, 0, 111, 135, 13, 2, 3, 23 }, + { 2077, 2249, 0, 103, 135, 11, 2, 3, 21 }, + { 2250, 2454, 0, 95, 103, 11, 1, 3, 19 }, + { 2455, 2699, 0, 87, 103, 9, 1, 3, 18 }, + { 2700, 2999, 0, 79, 103, 9, 1, 2, 16 }, + { 3000, 3229, 0, 71, 71, 7, 1, 2, 15 }, + { 3230, 3599, 1, 87, 103, 9, 1, 3, 18 }, + { 3600, 3999, 1, 79, 103, 9, 1, 2, 16 }, + { 4000, 4499, 1, 71, 103, 7, 1, 2, 15 }, + { 4500, 4500, 1, 63, 71, 7, 0, 2, 13 }, + { } +}; + +static const struct i_thssettle_params table2[] = { + { 80, 124, 24 }, + { 125, 249, 20 }, + { 250, 499, 16 }, + { 500, 749, 14 }, + { 750, 1499, 13 }, + { 1500, 4500, 12 }, + { } +}; + +static const struct oa_lane_clk_div_params table6[] = { + { 80, 159, 0x1 }, + { 160, 319, 0x2 }, + { 320, 639, 0x3 }, + { 640, 1279, 0x4 }, + { 1280, 2560, 0x5 }, + { 2561, 4500, 0x6 }, + { } +}; + +static const struct cdr_fbk_cap_prog_params table7[] = { + { 80, 919, 0 }, + { 920, 1029, 1 }, + { 1030, 1169, 2 }, + { 1170, 1349, 3 }, + { 1350, 1589, 4 }, + { 1590, 1949, 5 }, + { 1950, 2499, 6 }, + { 2500, 3500, 7 }, + { } +}; + +static void dwc_phy_write(struct ipu6_isys *isys, u32 id, u32 addr, u16 data) +{ + void __iomem *isys_base = isys->pdata->base; + void __iomem *base = isys_base + IPU7_IS_IO_CDPHY_BASE(id); + + dev_dbg(&isys->adev->auxdev.dev, "phy write: reg 0x%lx = data 0x%04x", + (unsigned long)(base + addr - isys_base), data); + writew(data, base + addr); +} + +static u16 dwc_phy_read(struct ipu6_isys *isys, u32 id, u32 addr) +{ + void __iomem *isys_base = isys->pdata->base; + void __iomem *base = isys_base + IPU7_IS_IO_CDPHY_BASE(id); + u16 data; + + data = readw(base + addr); + dev_dbg(&isys->adev->auxdev.dev, "phy read: reg 0x%lx = data 0x%04x", + (unsigned long)(base + addr - isys_base), data); + + return data; +} + +static void dwc_csi_write(struct ipu6_isys *isys, u32 id, u32 addr, u32 data) +{ + void __iomem *isys_base = isys->pdata->base; + void __iomem *base = isys_base + IPU7_IS_IO_CSI2_HOST_BASE(id); + struct device *dev = &isys->adev->auxdev.dev; + + dev_dbg(dev, "csi write: reg 0x%lx = data 0x%08x", + (unsigned long)(base + addr - isys_base), data); + writel(data, base + addr); + dev_dbg(dev, "csi read: reg 0x%lx = data 0x%08x", + (unsigned long)(base + addr - isys_base), + readl(base + addr)); +} + +static void gpreg_write(struct ipu6_isys *isys, u32 id, u32 addr, u32 data) +{ + void __iomem *isys_base = isys->pdata->base; + u32 gpreg = isys->pdata->ipdata->csi2.gpreg; + void __iomem *base = isys_base + gpreg + 0x1000 * id; + struct device *dev = &isys->adev->auxdev.dev; + + dev_dbg(dev, "gpreg write: reg 0x%lx = data 0x%08x", + (unsigned long)(base + addr - isys_base), data); + writel(data, base + addr); + dev_dbg(dev, "gpreg read: reg 0x%lx = data 0x%08x", + (unsigned long)(base + addr - isys_base), + readl(base + addr)); +} + +static u32 dwc_csi_read(struct ipu6_isys *isys, u32 id, u32 addr) +{ + void __iomem *isys_base = isys->pdata->base; + void __iomem *base = isys_base + IPU7_IS_IO_CSI2_HOST_BASE(id); + u32 data; + + data = readl(base + addr); + dev_dbg(&isys->adev->auxdev.dev, "csi read: reg 0x%lx = data 0x%x", + (unsigned long)(base + addr - isys_base), data); + + return data; +} + +static void dwc_phy_write_mask(struct ipu6_isys *isys, u32 id, u32 addr, + u16 val, u8 lo, u8 hi) +{ + u32 temp, mask; + + WARN_ON(lo > hi); + WARN_ON(hi > 15); + + mask = ((~0U - (1U << lo) + 1U)) & (~0U >> (31 - hi)); + temp = dwc_phy_read(isys, id, addr); + temp &= ~mask; + temp |= (val << lo) & mask; + dwc_phy_write(isys, id, addr, temp); +} + +static void dwc_csi_write_mask(struct ipu6_isys *isys, u32 id, u32 addr, + u32 val, u8 hi, u8 lo) +{ + u32 temp, mask; + + WARN_ON(lo > hi); + + mask = ((~0U - (1U << lo) + 1U)) & (~0U >> (31 - hi)); + temp = dwc_csi_read(isys, id, addr); + temp &= ~mask; + temp |= (val << lo) & mask; + dwc_csi_write(isys, id, addr, temp); +} + +static void ipu7_isys_csi_ctrl_cfg(struct ipu6_isys_csi2 *csi2) +{ + struct ipu6_isys *isys = csi2->isys; + struct device *dev = &isys->adev->auxdev.dev; + u32 id, lanes, phy_mode; + u32 val; + + id = csi2->port; + lanes = csi2->nlanes; + phy_mode = csi2->phy_mode; + dev_dbg(dev, "csi-%d controller init with %u lanes, phy mode %u", + id, lanes, phy_mode); + + val = dwc_csi_read(isys, id, IPU7_VERSION); + dev_dbg(dev, "csi-%d controller version = 0x%x", id, val); + + /* num of active data lanes */ + dwc_csi_write(isys, id, IPU7_N_LANES, lanes - 1); + dwc_csi_write(isys, id, IPU7_CDPHY_MODE, phy_mode); + dwc_csi_write(isys, id, IPU7_VC_EXTENSION, 0); + + /* only mask PHY_FATAL and PKT_FATAL interrupts */ + dwc_csi_write(isys, id, IPU7_INT_MSK_PHY_FATAL, 0xff); + dwc_csi_write(isys, id, IPU7_INT_MSK_PKT_FATAL, 0x3); + dwc_csi_write(isys, id, IPU7_INT_MSK_PHY, 0x0); + dwc_csi_write(isys, id, IPU7_INT_MSK_LINE, 0x0); + dwc_csi_write(isys, id, IPU7_INT_MSK_BNDRY_FRAME_FATAL, 0x0); + dwc_csi_write(isys, id, IPU7_INT_MSK_SEQ_FRAME_FATAL, 0x0); + dwc_csi_write(isys, id, IPU7_INT_MSK_CRC_FRAME_FATAL, 0x0); + dwc_csi_write(isys, id, IPU7_INT_MSK_PLD_CRC_FATAL, 0x0); + dwc_csi_write(isys, id, IPU7_INT_MSK_DATA_ID, 0x0); + dwc_csi_write(isys, id, IPU7_INT_MSK_ECC_CORRECTED, 0x0); +} + +static void ipu7_isys_csi_phy_reset(struct ipu6_isys *isys, u32 id) +{ + dwc_csi_write(isys, id, IPU7_PHY_SHUTDOWNZ, 0); + dwc_csi_write(isys, id, IPU7_DPHY_RSTZ, 0); + dwc_csi_write(isys, id, IPU7_CSI2_RESETN, 0); + gpreg_write(isys, id, IPU7_PHY_RESET, 0); + gpreg_write(isys, id, IPU7_PHY_SHUTDOWN, 0); +} + +/* 8 Data ID monitors, each Data ID is composed by pair of VC and data type */ +static int __dids_config(struct ipu6_isys_csi2 *csi2, u32 id, u8 vc, u8 dt) +{ + struct ipu6_isys *isys = csi2->isys; + u32 reg, n; + u8 lo, hi; + int ret; + + dev_dbg(&isys->adev->auxdev.dev, + "config CSI-%u with vc:%u dt:0x%02x\n", id, vc, dt); + + dwc_csi_write(isys, id, IPU7_VC_EXTENSION, 0x0); + n = find_first_zero_bit(data_ids, N_DATA_IDS); + if (n == N_DATA_IDS) + return -ENOSPC; + + ret = test_and_set_bit(n, data_ids); + if (ret) + return -EBUSY; + + reg = n < 4 ? IPU7_DATA_IDS_VC_1 : IPU7_DATA_IDS_VC_2; + lo = (n % 4) * 8; + hi = lo + 4; + dwc_csi_write_mask(isys, id, reg, vc & GENMASK(4, 0), hi, lo); + + reg = n < 4 ? IPU7_DATA_IDS_1 : IPU7_DATA_IDS_2; + lo = (n % 4) * 8; + hi = lo + 5; + dwc_csi_write_mask(isys, id, reg, dt & GENMASK(5, 0), hi, lo); + + return 0; +} + +static int ipu7_isys_csi_ctrl_dids_config(struct ipu6_isys_csi2 *csi2, u32 id) +{ + struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; + struct device *dev = &csi2->isys->adev->auxdev.dev; + struct v4l2_mbus_frame_desc desc; + struct v4l2_subdev *ext_sd; + struct media_pad *pad; + int ret; + + pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity); + if (IS_ERR(pad)) { + dev_warn(dev, "can't get remote source pad of %s (%pe)\n", + csi2->asd.sd.name, pad); + return PTR_ERR(pad); + } + + ext_sd = media_entity_to_v4l2_subdev(pad->entity); + if (WARN(!ext_sd, "Failed to get subdev for entity %s\n", + pad->entity->name)) + return -ENODEV; + + ret = v4l2_subdev_call(ext_sd, pad, get_frame_desc, pad->index, &desc); + if (ret) + return ret; + + if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + dev_warn(dev, "Unsupported frame descriptor type\n"); + return -EINVAL; + } + + for (unsigned int i = 0; i < desc.num_entries; i++) { + desc_entry = &desc.entry[i]; + if (desc_entry->bus.csi2.vc < NR_OF_CSI2_VC) { + ret = __dids_config(csi2, id, desc_entry->bus.csi2.vc, + desc_entry->bus.csi2.dt); + if (ret) + return ret; + } + } + + return 0; +} + +#define CDPHY_TIMEOUT 5000000U +static int ipu7_isys_phy_ready(struct ipu6_isys *isys, u32 id) +{ + void __iomem *isys_base = isys->pdata->base; + u32 gpreg_offset = isys->pdata->ipdata->csi2.gpreg; + void __iomem *gpreg = isys_base + gpreg_offset + 0x1000 * id; + struct device *dev = &isys->adev->auxdev.dev; + u32 phy_ready; + u32 reg, rext; + int ret; + + dev_dbg(dev, "waiting phy ready...\n"); + ret = readl_poll_timeout(gpreg + IPU7_PHY_READY, phy_ready, + phy_ready & BIT(0) && phy_ready != ~0U, + 100, CDPHY_TIMEOUT); + dev_dbg(dev, "phy %u ready = 0x%08x\n", + id, readl(gpreg + IPU7_PHY_READY)); + dev_dbg(dev, "csi %u IPU7_PHY_RX = 0x%08x\n", id, + dwc_csi_read(isys, id, IPU7_PHY_RX)); + dev_dbg(dev, "csi %u IPU7_PHY_STOPSTATE = 0x%08x\n", id, + dwc_csi_read(isys, id, IPU7_PHY_STOPSTATE)); + dev_dbg(dev, "csi %u IPU7_PHY_CAL = 0x%08x\n", id, + dwc_csi_read(isys, id, IPU7_PHY_CAL)); + for (unsigned int i = 0; i < 4U; i++) { + reg = IPU7_CORE_DIG_DLANE_0_R_HS_RX_0 + (i * 0x400U); + dev_dbg(dev, "phy %u DLANE%u skewcal = 0x%04x\n", + id, i, dwc_phy_read(isys, id, reg)); + } + dev_dbg(dev, "phy %u DDLCAL = 0x%04x\n", id, + dwc_phy_read(isys, id, + IPU7_PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_5)); + dev_dbg(dev, "phy %u TERMCAL = 0x%04x\n", id, + dwc_phy_read(isys, id, IPU7_PPI_R_TERMCAL_DEBUG_0)); + dev_dbg(dev, "phy %u LPDCOCAL = 0x%04x\n", id, + dwc_phy_read(isys, id, IPU7_PPI_R_LPDCOCAL_DEBUG_RB)); + dev_dbg(dev, "phy %u HSDCOCAL = 0x%04x\n", id, + dwc_phy_read(isys, id, IPU7_PPI_R_HSDCOCAL_DEBUG_RB)); + dev_dbg(dev, "phy %u LPDCOCAL_VT = 0x%04x\n", id, + dwc_phy_read(isys, id, IPU7_PPI_R_LPDCOCAL_DEBUG_VT)); + + if (!ret) { + if (id) { + dev_dbg(dev, "ignore phy %u rext\n", id); + return 0; + } + + rext = dwc_phy_read(isys, id, + IPU7_CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_15) & + 0xfU; + dev_dbg(dev, "phy %u rext value = %u\n", id, rext); + isys->phy_rext_cal = (rext ? rext : 5); + + return 0; + } + + dev_err(dev, "wait phy ready timeout!\n"); + + return ret; +} + +static int lookup_table1(u64 mbps) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(table1); i++) { + if (mbps >= table1[i].min_mbps && mbps <= table1[i].max_mbps) + return i; + } + + return -ENXIO; +} + +static const u16 deskew_fine_mem[] = { + 0x0404, 0x040c, 0x0414, 0x041c, + 0x0423, 0x0429, 0x0430, 0x043a, + 0x0445, 0x044a, 0x0450, 0x045a, + 0x0465, 0x0469, 0x0472, 0x047a, + 0x0485, 0x0489, 0x0490, 0x049a, + 0x04a4, 0x04ac, 0x04b4, 0x04bc, + 0x04c4, 0x04cc, 0x04d4, 0x04dc, + 0x04e4, 0x04ec, 0x04f4, 0x04fc, + 0x0504, 0x050c, 0x0514, 0x051c, + 0x0523, 0x0529, 0x0530, 0x053a, + 0x0545, 0x054a, 0x0550, 0x055a, + 0x0565, 0x0569, 0x0572, 0x057a, + 0x0585, 0x0589, 0x0590, 0x059a, + 0x05a4, 0x05ac, 0x05b4, 0x05bc, + 0x05c4, 0x05cc, 0x05d4, 0x05dc, + 0x05e4, 0x05ec, 0x05f4, 0x05fc, + 0x0604, 0x060c, 0x0614, 0x061c, + 0x0623, 0x0629, 0x0632, 0x063a, + 0x0645, 0x064a, 0x0650, 0x065a, + 0x0665, 0x0669, 0x0672, 0x067a, + 0x0685, 0x0689, 0x0690, 0x069a, + 0x06a4, 0x06ac, 0x06b4, 0x06bc, + 0x06c4, 0x06cc, 0x06d4, 0x06dc, + 0x06e4, 0x06ec, 0x06f4, 0x06fc, + 0x0704, 0x070c, 0x0714, 0x071c, + 0x0723, 0x072a, 0x0730, 0x073a, + 0x0745, 0x074a, 0x0750, 0x075a, + 0x0765, 0x0769, 0x0772, 0x077a, + 0x0785, 0x0789, 0x0790, 0x079a, + 0x07a4, 0x07ac, 0x07b4, 0x07bc, + 0x07c4, 0x07cc, 0x07d4, 0x07dc, + 0x07e4, 0x07ec, 0x07f4, 0x07fc, +}; + +static void ipu7_isys_dphy_config(struct ipu6_isys *isys, u8 id, u8 lanes, + bool aggregation, u64 mbps) +{ + struct ipu6_device *isp = isys->adev->isp; + u16 hsrxval0 = 0; + u16 hsrxval1 = 0; + u16 hsrxval2 = 0; + int index; + u16 reg; + u16 val; + u32 i; + + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_RW_COMMON_7, 0, 0, 9); + if (mbps > 1500) + dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_DPHY_7, + 40, 0, 7); + else + dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_DPHY_7, + 104, 0, 7); + + dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_DPHY_8, + 80, 0, 7); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_0, 191, 0, 9); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_7, 34, 7, 12); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_1, 38, 8, 15); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_2, 4, 12, 15); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_2, 2, 10, 11); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_2, 1, 8, 8); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_2, 38, 0, 7); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_2, 1, 9, 9); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_4, 10, 0, 9); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_6, 20, 0, 9); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_7, 19, 0, 6); + + for (i = 0; i < ARRAY_SIZE(table0); i++) { + if (mbps >= table0[i].min_mbps && mbps <= table0[i].max_mbps) { + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_3, + table0[i].ddlcal_counter_ref, + 0, 9); + break; + } + } + + index = lookup_table1(mbps); + if (index >= 0) { + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_1, + table1[index].phase_bound, 0, 7); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_5, + table1[index].ddlcal_dll_fbk, 4, 9); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_5, + table1[index].ddlcal_ddl_coarse_bank, 0, 3); + + reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_8; + val = table1[index].oa_lanex_hsrx_cdphy_sel_fast; + for (i = 0; i < lanes + 1; i++) + dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, + 12, 12); + } + + reg = IPU7_CORE_DIG_DLANE_0_RW_LP_0; + for (i = 0; i < lanes; i++) + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11); + + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2, + 0, 0, 0); + if (!IS_IPU7_MTL(isp) || id == PORT_B || id == PORT_C) { + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, + 1, 0, 0); + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, + 0, 0, 0); + } else { + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, + 0, 0, 0); + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, + 1, 0, 0); + } + + if (lanes == 4 && IS_IPU7_MTL(isp)) { + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2, + 0, 0, 0); + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2, + 0, 0, 0); + } + + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_RW_COMMON_6, 1, 0, 2); + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_RW_COMMON_6, 1, 3, 5); + + reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_12; + val = (mbps > 1500) ? 0 : 1; + for (i = 0; i < lanes + 1; i++) { + dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); + dwc_phy_write_mask(isys, id, reg + (i * 0x400), !val, 3, 3); + } + + reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_13; + val = (mbps > 1500) ? 0 : 1; + for (i = 0; i < lanes + 1; i++) { + dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); + dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 3, 3); + } + + if (!IS_IPU7_MTL(isp) || id == PORT_B || id == PORT_C) + reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9; + else + reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9; + + for (i = 0; i < ARRAY_SIZE(table6); i++) { + if (mbps >= table6[i].min_mbps && mbps <= table6[i].max_mbps) { + dwc_phy_write_mask(isys, id, reg, + table6[i].oa_lane_hsrx_hs_clk_div, + 5, 7); + break; + } + } + + if (aggregation) { + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_RW_COMMON_0, 1, + 1, 1); + + reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15; + dwc_phy_write_mask(isys, id, reg, 3, 3, 4); + + val = (id == PORT_A) ? 3 : 0; + reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15; + dwc_phy_write_mask(isys, id, reg, val, 3, 4); + + reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15; + dwc_phy_write_mask(isys, id, reg, 3, 3, 4); + } + + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_0, + 28, 0, 7); + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_7, + 6, 0, 7); + + reg = IPU7_CORE_DIG_DLANE_0_RW_HS_RX_0; + for (i = 0; i < ARRAY_SIZE(table2); i++) { + if (mbps >= table2[i].min_mbps && mbps <= table2[i].max_mbps) { + u8 j; + + for (j = 0; j < lanes; j++) + dwc_phy_write_mask(isys, id, reg + (j * 0x400), + table2[i].i_thssettle, + 8, 15); + break; + } + } + + /* deskew */ + for (i = 0; i < lanes; i++) { + reg = IPU7_CORE_DIG_DLANE_0_RW_CFG_1; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), + ((mbps > 1500) ? 0x1 : 0x2), 2, 3); + + reg = IPU7_CORE_DIG_DLANE_0_RW_HS_RX_2; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), + ((mbps > 2500) ? 0 : 1), 15, 15); + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 13, 13); + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 7, 9, 12); + + reg = IPU7_CORE_DIG_DLANE_0_RW_LP_0; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 12, 15); + + reg = IPU7_CORE_DIG_DLANE_0_RW_LP_2; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 0, 0); + + reg = IPU7_CORE_DIG_DLANE_0_RW_HS_RX_1; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 16, 0, 7); + + reg = IPU7_CORE_DIG_DLANE_0_RW_HS_RX_3; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 2, 0, 2); + index = lookup_table1(mbps); + if (index >= 0) { + val = table1[index].fjump_deskew; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, + 3, 8); + } + + reg = IPU7_CORE_DIG_DLANE_0_RW_HS_RX_4; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 150, 0, 15); + + reg = IPU7_CORE_DIG_DLANE_0_RW_HS_RX_5; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 0, 7); + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 8, 15); + + reg = IPU7_CORE_DIG_DLANE_0_RW_HS_RX_6; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 2, 0, 7); + index = lookup_table1(mbps); + if (index >= 0) { + val = table1[index].min_eye_opening_deskew; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, + 8, 15); + } + reg = IPU7_CORE_DIG_DLANE_0_RW_HS_RX_7; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 13, 13); + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 15, 15); + + reg = IPU7_CORE_DIG_DLANE_0_RW_HS_RX_9; + index = lookup_table1(mbps); + if (index >= 0) { + val = table1[index].ddlcal_max_phase; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), + val, 0, 7); + } + } + + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_DLANE_CLK_RW_LP_0, + 1, 12, 15); + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_DLANE_CLK_RW_LP_2, 0, 0, 0); + + for (i = 0; i < ARRAY_SIZE(deskew_fine_mem); i++) + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_COMMON_RW_DESKEW_FINE_MEM, + deskew_fine_mem[i], 0, 15); + + if (mbps > 1500) { + hsrxval0 = 4; + hsrxval2 = 3; + } + + if (mbps > 2500) + hsrxval1 = 2; + + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9, + hsrxval0, 0, 2); + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9, + hsrxval0, 0, 2); + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9, + hsrxval0, 0, 2); + if (lanes == 4 && IS_IPU7_MTL(isp)) { + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9, + hsrxval0, 0, 2); + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9, + hsrxval0, 0, 2); + } + + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9, + hsrxval1, 3, 4); + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9, + hsrxval1, 3, 4); + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9, + hsrxval1, 3, 4); + if (lanes == 4 && IS_IPU7_MTL(isp)) { + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9, + hsrxval1, 3, 4); + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9, + hsrxval1, 3, 4); + } + + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15, + hsrxval2, 0, 2); + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15, + hsrxval2, 0, 2); + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15, + hsrxval2, 0, 2); + if (lanes == 4 && IS_IPU7_MTL(isp)) { + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_15, + hsrxval2, 0, 2); + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_15, + hsrxval2, 0, 2); + } + + /* force and override rext */ + if (isys->phy_rext_cal && id) { + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8, + isys->phy_rext_cal, 0, 3); + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7, + 1, 11, 11); + } +} + +static void ipu7_isys_cphy_config(struct ipu6_isys *isys, u8 id, u8 lanes, + bool aggregation, u64 mbps) +{ + struct ipu6_device *isp = isys->adev->isp; + u8 trios = 2; + u16 coarse_target; + u16 deass_thresh; + u16 delay_thresh; + u16 reset_thresh; + u16 cap_prog = 6U; + u16 reg; + u16 val; + u32 i; + u64 r64; + u32 r; + + if (IS_IPU7P5(isp)) + val = 0x15; + else + val = 0x155; + + if (IS_IPU7_MTL(isp)) + trios = 3; + + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_RW_COMMON_7, val, 0, 9); + dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_DPHY_7, + 104, 0, 7); + dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_DPHY_8, + 16, 0, 7); + + reg = IPU7_CORE_DIG_CLANE_0_RW_LP_0; + for (i = 0; i < trios; i++) + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11); + + val = (mbps > 900U) ? 1U : 0U; + for (i = 0; i < trios; i++) { + reg = IPU7_CORE_DIG_CLANE_0_RW_HS_RX_0; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 0, 0); + dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); + + reg = IPU7_CORE_DIG_CLANE_0_RW_HS_RX_1; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 38, 0, 15); + + reg = IPU7_CORE_DIG_CLANE_0_RW_HS_RX_5; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 38, 0, 15); + + reg = IPU7_CORE_DIG_CLANE_0_RW_HS_RX_6; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 10, 0, 15); + } + + /* + * Below 900Msps, always use the same value. + * The formula is suitable for data rate 80-3500Msps. + * Timebase (us) = 1, DIV = 32, TDDL (UI) = 0.5 + */ + if (mbps >= 80U) + coarse_target = DIV_ROUND_UP_ULL(mbps, 16) - 1; + else + coarse_target = 56; + + for (i = 0; i < trios; i++) { + reg = IPU7_CORE_DIG_CLANE_0_RW_HS_RX_2 + i * 0x400; + dwc_phy_write_mask(isys, id, reg, coarse_target, 0, 15); + } + + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2, 1, 0, 0); + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, 0, 0, 0); + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, 1, 0, 0); + + if (!IS_IPU7P5(isp) && lanes == 4) { + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2, + 1, 0, 0); + dwc_phy_write_mask(isys, id, + IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2, + 0, 0, 0); + } + + for (i = 0; i < trios; i++) { + reg = IPU7_CORE_DIG_RW_TRIO0_0 + i * 0x400; + dwc_phy_write_mask(isys, id, reg, 1, 6, 8); + dwc_phy_write_mask(isys, id, reg, 1, 3, 5); + dwc_phy_write_mask(isys, id, reg, 2, 0, 2); + } + + deass_thresh = (u16)div64_u64_rem(7 * 1000 * 6, mbps * 5U, &r64) + 1; + if (r64 != 0) + deass_thresh++; + + reg = IPU7_CORE_DIG_RW_TRIO0_2; + for (i = 0; i < trios; i++) + dwc_phy_write_mask(isys, id, reg + 0x400 * i, + deass_thresh, 0, 7); + + delay_thresh = div64_u64((224U - (9U * 7U)) * 1000U, 5U * mbps) - 7u; + + if (delay_thresh < 1) + delay_thresh = 1; + + reg = IPU7_CORE_DIG_RW_TRIO0_1; + for (i = 0; i < trios; i++) + dwc_phy_write_mask(isys, id, reg + 0x400 * i, + delay_thresh, 0, 15); + + reset_thresh = (u16)div_u64_rem(2U * 5U * mbps, 7U * 1000U, &r); + if (!r) + reset_thresh--; + + if (reset_thresh < 1) + reset_thresh = 1; + + reg = IPU7_CORE_DIG_RW_TRIO0_0; + for (i = 0; i < trios; i++) + dwc_phy_write_mask(isys, id, reg + 0x400 * i, + reset_thresh, 9, 11); + + /* Tuning ITMINRX to 2 for CPHY */ + reg = IPU7_CORE_DIG_CLANE_0_RW_LP_0; + for (i = 0; i < trios; i++) + dwc_phy_write_mask(isys, id, reg + 0x400 * i, 2, 12, 15); + + reg = IPU7_CORE_DIG_CLANE_0_RW_LP_2; + for (i = 0; i < trios; i++) + dwc_phy_write_mask(isys, id, reg + 0x400 * i, 0, 0, 0); + + reg = IPU7_CORE_DIG_CLANE_0_RW_HS_RX_0; + for (i = 0; i < trios; i++) + dwc_phy_write_mask(isys, id, reg + 0x400 * i, 12, 2, 6); + + for (i = 0; i < ARRAY_SIZE(table7); i++) { + if (mbps >= table7[i].min_mbps && mbps <= table7[i].max_mbps) { + cap_prog = table7[i].val; + break; + } + } + + for (i = 0; i < (lanes + 1); i++) { + reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 + 0x400 * i; + dwc_phy_write_mask(isys, id, reg, 4U, 0, 2); + /* Set GMODE to 2 when CPHY >= 1.5Gsps */ + if (mbps >= 1500) + dwc_phy_write_mask(isys, id, reg, 2U, 3, 4); + else + dwc_phy_write_mask(isys, id, reg, 0U, 3, 4); + + reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 + 0x400 * i; + dwc_phy_write_mask(isys, id, reg, cap_prog, 10, 12); + } +} + +static int ipu7_isys_phy_config(struct ipu6_isys *isys, u8 id, u8 lanes, + bool aggregation) +{ + struct device *dev = &isys->adev->auxdev.dev; + u32 phy_mode; + s64 link_freq; + u64 mbps; + + if (aggregation) + link_freq = ipu6_isys_csi2_get_link_freq(&isys->csi2[0]); + else + link_freq = ipu6_isys_csi2_get_link_freq(&isys->csi2[id]); + + if (link_freq < 0) { + dev_err(dev, "get link freq failed (%lld)\n", link_freq); + return link_freq; + } + + mbps = div_u64(link_freq, 500000); + dev_dbg(dev, "config phy %u with lanes %u aggregation %d mbps %lld\n", + id, lanes, aggregation, mbps); + + dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_DPHY_10, + 48, 0, 7); + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_2, + 1, 12, 13); + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_0, + 63, 2, 7); + dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_STARTUP_1_1, + 563, 0, 11); + dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_DPHY_2, + 5, 0, 7); + /* bypass the RCAL state (bit6) */ + if (aggregation && id != PORT_A) + dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_DPHY_2, + 0x45, 0, 7); + + dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_DPHY_6, + 39, 0, 7); + dwc_phy_write_mask(isys, id, IPU7_PPI_CALIBCTRL_RW_COMMON_BG_0, + 500, 0, 8); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_TERMCAL_CFG_0, 38, 0, 6); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_OFFSETCAL_CFG_0, 7, 0, 4); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_LPDCOCAL_TIMEBASE, 153, 0, 9); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_LPDCOCAL_NREF, 800, 0, 10); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_LPDCOCAL_NREF_RANGE, + 27, 0, 4); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_LPDCOCAL_TWAIT_CONFIG, + 47, 0, 8); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_LPDCOCAL_TWAIT_CONFIG, + 127, 9, 15); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_LPDCOCAL_VT_CONFIG, 47, 7, 15); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_LPDCOCAL_VT_CONFIG, 27, 2, 6); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_LPDCOCAL_VT_CONFIG, 3, 0, 1); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_LPDCOCAL_COARSE_CFG, 1, 0, 1); + dwc_phy_write_mask(isys, id, IPU7_PPI_RW_COMMON_CFG, 3, 0, 1); + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0, + 0, 10, 10); + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1, + 1, 10, 10); + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1, + 0, 15, 15); + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_3, + 3, 8, 9); + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0, + 0, 15, 15); + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_6, + 7, 12, 14); + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7, + 0, 8, 10); + /* resistance tuning: 1 for 45ohm, 0 for 50ohm */ + dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5, + 1, 8, 8); + + if (aggregation) + phy_mode = isys->csi2[0].phy_mode; + else + phy_mode = isys->csi2[id].phy_mode; + + if (phy_mode == PHY_MODE_DPHY) { + ipu7_isys_dphy_config(isys, id, lanes, aggregation, mbps); + } else if (phy_mode == PHY_MODE_CPHY) { + ipu7_isys_cphy_config(isys, id, lanes, aggregation, mbps); + } else { + dev_err(dev, "unsupported phy mode %d!\n", + isys->csi2[id].phy_mode); + } + + return 0; +} + +static int ipu7_isys_csi_phy_powerup(struct ipu6_isys_csi2 *csi2) +{ + struct ipu6_isys *isys = csi2->isys; + u32 lanes = csi2->nlanes; + bool aggregation = false; + u32 id = csi2->port; + int ret; + + /* lanes remapping for aggregation (port AB) mode */ + if (!IS_IPU7_MTL(isys->adev->isp) && lanes > 2 && id == PORT_A) { + aggregation = true; + lanes = 2; + } + + ipu7_isys_csi_phy_reset(isys, id); + gpreg_write(isys, id, IPU7_PHY_CLK_LANE_CONTROL, 0x1); + gpreg_write(isys, id, IPU7_PHY_CLK_LANE_FORCE_CONTROL, 0x2); + gpreg_write(isys, id, IPU7_PHY_LANE_CONTROL_EN, (1U << lanes) - 1U); + gpreg_write(isys, id, IPU7_PHY_LANE_FORCE_CONTROL, 0xf); + gpreg_write(isys, id, IPU7_PHY_MODE, csi2->phy_mode); + + /* config PORT_B if aggregation mode */ + if (aggregation) { + ipu7_isys_csi_phy_reset(isys, PORT_B); + gpreg_write(isys, PORT_B, IPU7_PHY_CLK_LANE_CONTROL, 0x0); + gpreg_write(isys, PORT_B, IPU7_PHY_LANE_CONTROL_EN, 0x3); + gpreg_write(isys, PORT_B, IPU7_PHY_CLK_LANE_FORCE_CONTROL, 0x2); + gpreg_write(isys, PORT_B, IPU7_PHY_LANE_FORCE_CONTROL, 0xf); + gpreg_write(isys, PORT_B, IPU7_PHY_MODE, csi2->phy_mode); + } + + ipu7_isys_csi_ctrl_cfg(csi2); + ipu7_isys_csi_ctrl_dids_config(csi2, id); + + ret = ipu7_isys_phy_config(isys, id, lanes, aggregation); + if (ret < 0) + return ret; + + gpreg_write(isys, id, IPU7_PHY_RESET, 1); + gpreg_write(isys, id, IPU7_PHY_SHUTDOWN, 1); + dwc_csi_write(isys, id, IPU7_DPHY_RSTZ, 1); + dwc_csi_write(isys, id, IPU7_PHY_SHUTDOWNZ, 1); + dwc_csi_write(isys, id, IPU7_CSI2_RESETN, 1); + + ret = ipu7_isys_phy_ready(isys, id); + if (ret < 0) + return ret; + + gpreg_write(isys, id, IPU7_PHY_LANE_FORCE_CONTROL, 0); + gpreg_write(isys, id, IPU7_PHY_CLK_LANE_FORCE_CONTROL, 0); + + /* config PORT_B if aggregation mode */ + if (aggregation) { + ret = ipu7_isys_phy_config(isys, PORT_B, 2, aggregation); + if (ret < 0) + return ret; + + gpreg_write(isys, PORT_B, IPU7_PHY_RESET, 1); + gpreg_write(isys, PORT_B, IPU7_PHY_SHUTDOWN, 1); + dwc_csi_write(isys, PORT_B, IPU7_DPHY_RSTZ, 1); + dwc_csi_write(isys, PORT_B, IPU7_PHY_SHUTDOWNZ, 1); + dwc_csi_write(isys, PORT_B, IPU7_CSI2_RESETN, 1); + ret = ipu7_isys_phy_ready(isys, PORT_B); + if (ret < 0) + return ret; + + gpreg_write(isys, PORT_B, IPU7_PHY_LANE_FORCE_CONTROL, 0); + gpreg_write(isys, PORT_B, IPU7_PHY_CLK_LANE_FORCE_CONTROL, 0); + } + + return 0; +} + +static void ipu7_isys_csi_phy_powerdown(struct ipu6_isys_csi2 *csi2) +{ + struct ipu6_isys *isys = csi2->isys; + + ipu7_isys_csi_phy_reset(isys, csi2->port); + if (!IS_IPU7_MTL(isys->adev->isp) && + csi2->nlanes > 2U && csi2->port == PORT_A) + ipu7_isys_csi_phy_reset(isys, PORT_B); +} + +int ipu7_isys_csi_phy_set_power(struct ipu6_isys *isys, + struct ipu6_isys_csi2_config *cfg, + const struct ipu6_isys_csi2_timing *timing, + bool on) +{ + struct ipu6_isys_csi2 *csi2 = &isys->csi2[cfg->port]; + + if (on) + return ipu7_isys_csi_phy_powerup(csi2); + + ipu7_isys_csi_phy_powerdown(csi2); + + return 0; +} diff --git a/drivers/media/pci/intel/ipu6/ipu7-isys-csi-phy.h b/drivers/media/pci/intel/ipu6/ipu7-isys-csi-phy.h new file mode 100644 index 000000000000..849fe888db07 --- /dev/null +++ b/drivers/media/pci/intel/ipu6/ipu7-isys-csi-phy.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2026 Intel Corporation */ + +#ifndef IPU7_ISYS_CSI_PHY_H +#define IPU7_ISYS_CSI_PHY_H + +struct ipu6_isys; +struct ipu6_isys_csi2_config; +struct ipu6_isys_csi2_timing; + +int ipu7_isys_csi_phy_set_power(struct ipu6_isys *isys, + struct ipu6_isys_csi2_config *cfg, + const struct ipu6_isys_csi2_timing *timing, + bool on); + +#endif /* IPU7_ISYS_CSI_PHY_H */ diff --git a/drivers/media/pci/intel/ipu6/ipu7-isys-csi2-regs.h b/drivers/media/pci/intel/ipu6/ipu7-isys-csi2-regs.h new file mode 100644 index 000000000000..3859401d4802 --- /dev/null +++ b/drivers/media/pci/intel/ipu6/ipu7-isys-csi2-regs.h @@ -0,0 +1,1189 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 - 2026 Intel Corporation + */ + +#ifndef IPU7_ISYS_CSI2_REG_H +#define IPU7_ISYS_CSI2_REG_H + +/* IS main regs base */ +#define IPU7_IS_MAIN_BASE 0x240000 +#define IPU7_IS_MAIN_S2B_BASE (IPU7_IS_MAIN_BASE + 0x22000) +#define IPU7_IS_MAIN_B2O_BASE (IPU7_IS_MAIN_BASE + 0x26000) +#define IPU7_IS_MAIN_ISD_M0_BASE (IPU7_IS_MAIN_BASE + 0x2b000) +#define IPU7_IS_MAIN_ISD_M1_BASE (IPU7_IS_MAIN_BASE + 0x2b100) +#define IPU7_IS_MAIN_ISD_INT_BASE (IPU7_IS_MAIN_BASE + 0x2b200) +#define IPU7_IS_MAIN_GDA_BASE (IPU7_IS_MAIN_BASE + 0x32000) +#define IPU7_IS_MAIN_GPREGS_MAIN_BASE (IPU7_IS_MAIN_BASE + 0x32500) +#define IPU7_IS_MAIN_IRQ_CTRL_BASE (IPU7_IS_MAIN_BASE + 0x32700) +#define IPU7_IS_MAIN_PWM_CTRL_BASE (IPU7_IS_MAIN_BASE + 0x32b00) + +#define IPU7_S2B_IRQ_COMMON_0_CTL_STATUS (IPU7_IS_MAIN_S2B_BASE + 0x1c) +#define IPU7_S2B_IRQ_COMMON_0_CTL_CLEAR (IPU7_IS_MAIN_S2B_BASE + 0x20) +#define IPU7_S2B_IRQ_COMMON_0_CTL_ENABLE (IPU7_IS_MAIN_S2B_BASE + 0x24) +#define IPU7_S2B_IID_IRQ_CTL_STATUS(iid) (IPU7_IS_MAIN_S2B_BASE + 0x94 + \ + 0x100 * (iid)) + +#define IPU7_B2O_IRQ_COMMON_0_CTL_STATUS (IPU7_IS_MAIN_B2O_BASE + 0x30) +#define IPU7_B2O_IRQ_COMMON_0_CTL_CLEAR (IPU7_IS_MAIN_B2O_BASE + 0x34) +#define IPU7_B2O_IRQ_COMMON_0_CTL_ENABLE (IPU7_IS_MAIN_B2O_BASE + 0x38) +#define IPU7_B2O_IID_IRQ_CTL_STATUS(oid) (IPU7_IS_MAIN_B2O_BASE + 0x3dc + \ + 0x200 * (oid)) + +#define IPU7_ISD_M0_IRQ_CTL_STATUS (IPU7_IS_MAIN_ISD_M0_BASE + 0x1c) +#define IPU7_ISD_M0_IRQ_CTL_CLEAR (IPU7_IS_MAIN_ISD_M0_BASE + 0x20) +#define IPU7_ISD_M0_IRQ_CTL_ENABLE (IPU7_IS_MAIN_ISD_M0_BASE + 0x24) + +#define IPU7_ISD_M1_IRQ_CTL_STATUS (IPU7_IS_MAIN_ISD_M1_BASE + 0x1c) +#define IPU7_ISD_M1_IRQ_CTL_CLEAR (IPU7_IS_MAIN_ISD_M1_BASE + 0x20) +#define IPU7_ISD_M1_IRQ_CTL_ENABLE (IPU7_IS_MAIN_ISD_M1_BASE + 0x24) + +#define IPU7_ISD_INT_IRQ_CTL_STATUS (IPU7_IS_MAIN_ISD_INT_BASE + 0x1c) +#define IPU7_ISD_INT_IRQ_CTL_CLEAR (IPU7_IS_MAIN_ISD_INT_BASE + 0x20) +#define IPU7_ISD_INT_IRQ_CTL_ENABLE (IPU7_IS_MAIN_ISD_INT_BASE + 0x24) + +#define IPU7_GDA_IRQ_CTL_STATUS (IPU7_IS_MAIN_GDA_BASE + 0x1c) +#define IPU7_GDA_IRQ_CTL_CLEAR (IPU7_IS_MAIN_GDA_BASE + 0x20) +#define IPU7_GDA_IRQ_CTL_ENABLE (IPU7_IS_MAIN_GDA_BASE + 0x24) + +#define IPU7_IS_MAIN_IRQ_CTL_EDGE IPU7_IS_MAIN_IRQ_CTRL_BASE +#define IPU7_IS_MAIN_IRQ_CTL_MASK (IPU7_IS_MAIN_IRQ_CTRL_BASE + 0x4) +#define IPU7_IS_MAIN_IRQ_CTL_STATUS (IPU7_IS_MAIN_IRQ_CTRL_BASE + 0x8) +#define IPU7_IS_MAIN_IRQ_CTL_CLEAR (IPU7_IS_MAIN_IRQ_CTRL_BASE + 0xc) +#define IPU7_IS_MAIN_IRQ_CTL_ENABLE (IPU7_IS_MAIN_IRQ_CTRL_BASE + 0x10) +#define IPU7_IS_MAIN_IRQ_CTL_LEVEL_NOT_PULSE (IPU7_IS_MAIN_IRQ_CTRL_BASE + 0x14) + +/* IS IO regs base */ +#define IPU7_IS_PHY_NUM 4U +#define IPU7_IS_IO_BASE 0x280000 + +/* dwc csi cdphy registers */ +#define IPU7_IS_IO_CDPHY_BASE(i) (IPU7_IS_IO_BASE + 0x10000 * (i)) +#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_0 0x1800 +#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_1 0x1802 +#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_2 0x1804 +#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_3 0x1806 +#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_4 0x1808 +#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_5 0x180a +#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_6 0x180c +#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_7 0x180e +#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_8 0x1810 +#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_9 0x1812 +#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_A 0x1814 +#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_10 0x1820 +#define IPU7_PPI_STARTUP_RW_COMMON_STARTUP_1_1 0x1822 +#define IPU7_PPI_STARTUP_RW_COMMON_STARTUP_1_2 0x1824 +#define IPU7_PPI_CALIBCTRL_RW_COMMON_CALIBCTRL_2_0 0x1840 +#define IPU7_PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_1 0x1842 +#define IPU7_PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_2 0x1844 +#define IPU7_PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_3 0x1846 +#define IPU7_PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_4 0x1848 +#define IPU7_PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_5 0x184a +#define IPU7_PPI_CALIBCTRL_RW_COMMON_BG_0 0x184c +#define IPU7_PPI_CALIBCTRL_RW_COMMON_CALIBCTRL_2_7 0x184e +#define IPU7_PPI_CALIBCTRL_RW_ADC_CFG_0 0x1850 +#define IPU7_PPI_CALIBCTRL_RW_ADC_CFG_1 0x1852 +#define IPU7_PPI_CALIBCTRL_R_ADC_DEBUG 0x1854 +#define IPU7_PPI_RW_LPDCOCAL_TOP_OVERRIDE 0x1c00 +#define IPU7_PPI_RW_LPDCOCAL_TIMEBASE 0x1c02 +#define IPU7_PPI_RW_LPDCOCAL_NREF 0x1c04 +#define IPU7_PPI_RW_LPDCOCAL_NREF_RANGE 0x1c06 +#define IPU7_PPI_RW_LPDCOCAL_NREF_TRIGGER_MAN 0x1c08 +#define IPU7_PPI_RW_LPDCOCAL_TWAIT_CONFIG 0x1c0a +#define IPU7_PPI_RW_LPDCOCAL_VT_CONFIG 0x1c0c +#define IPU7_PPI_R_LPDCOCAL_DEBUG_RB 0x1c0e +#define IPU7_PPI_RW_LPDCOCAL_COARSE_CFG 0x1c10 +#define IPU7_PPI_R_LPDCOCAL_DEBUG_COARSE_RB 0x1c12 +#define IPU7_PPI_R_LPDCOCAL_DEBUG_COARSE_MEAS_0_RB 0x1c14 +#define IPU7_PPI_R_LPDCOCAL_DEBUG_COARSE_MEAS_1_RB 0x1c16 +#define IPU7_PPI_R_LPDCOCAL_DEBUG_COARSE_FWORD_RB 0x1c18 +#define IPU7_PPI_R_LPDCOCAL_DEBUG_MEASURE_CURR_ERROR 0x1c1a +#define IPU7_PPI_R_LPDCOCAL_DEBUG_MEASURE_LAST_ERROR 0x1c1c +#define IPU7_PPI_R_LPDCOCAL_DEBUG_VT 0x1c1e +#define IPU7_PPI_RW_LB_TIMEBASE_CONFIG 0x1c20 +#define IPU7_PPI_RW_LB_STARTCMU_CONFIG 0x1c22 +#define IPU7_PPI_R_LBPULSE_COUNTER_RB 0x1c24 +#define IPU7_PPI_R_LB_START_CMU_RB 0x1c26 +#define IPU7_PPI_RW_LB_DPHY_BURST_START 0x1c28 +#define IPU7_PPI_RW_LB_CPHY_BURST_START 0x1c2a +#define IPU7_PPI_RW_DDLCAL_CFG_0 0x1c40 +#define IPU7_PPI_RW_DDLCAL_CFG_1 0x1c42 +#define IPU7_PPI_RW_DDLCAL_CFG_2 0x1c44 +#define IPU7_PPI_RW_DDLCAL_CFG_3 0x1c46 +#define IPU7_PPI_RW_DDLCAL_CFG_4 0x1c48 +#define IPU7_PPI_RW_DDLCAL_CFG_5 0x1c4a +#define IPU7_PPI_RW_DDLCAL_CFG_6 0x1c4c +#define IPU7_PPI_RW_DDLCAL_CFG_7 0x1c4e +#define IPU7_PPI_R_DDLCAL_DEBUG_0 0x1c50 +#define IPU7_PPI_R_DDLCAL_DEBUG_1 0x1c52 +#define IPU7_PPI_RW_PARITY_TEST 0x1c60 +#define IPU7_PPI_RW_STARTUP_OVR_0 0x1c62 +#define IPU7_PPI_RW_STARTUP_STATE_OVR_1 0x1c64 +#define IPU7_PPI_RW_DTB_SELECTOR 0x1c66 +#define IPU7_PPI_RW_DPHY_CLK_SPARE 0x1c6a +#define IPU7_PPI_RW_COMMON_CFG 0x1c6c +#define IPU7_PPI_RW_TERMCAL_CFG_0 0x1c80 +#define IPU7_PPI_R_TERMCAL_DEBUG_0 0x1c82 +#define IPU7_PPI_RW_TERMCAL_CTRL_0 0x1c84 +#define IPU7_PPI_RW_OFFSETCAL_CFG_0 0x1ca0 +#define IPU7_PPI_R_OFFSETCAL_DEBUG_LANE0 0x1ca2 +#define IPU7_PPI_R_OFFSETCAL_DEBUG_LANE1 0x1ca4 +#define IPU7_PPI_R_OFFSETCAL_DEBUG_LANE2 0x1ca6 +#define IPU7_PPI_R_OFFSETCAL_DEBUG_LANE3 0x1ca8 +#define IPU7_PPI_R_OFFSETCAL_DEBUG_LANE4 0x1caa +#define IPU7_PPI_RW_HSDCOCAL_CFG_O 0x1d00 +#define IPU7_PPI_RW_HSDCOCAL_CFG_1 0x1d02 +#define IPU7_PPI_RW_HSDCOCAL_CFG_2 0x1d04 +#define IPU7_PPI_RW_HSDCOCAL_CFG_3 0x1d06 +#define IPU7_PPI_RW_HSDCOCAL_CFG_4 0x1d08 +#define IPU7_PPI_RW_HSDCOCAL_CFG_5 0x1d0a +#define IPU7_PPI_RW_HSDCOCAL_CFG_6 0x1d0c +#define IPU7_PPI_RW_HSDCOCAL_CFG_7 0x1d0e +#define IPU7_PPI_RW_HSDCOCAL_CFG_8 0x1d10 +#define IPU7_PPI_R_HSDCOCAL_DEBUG_RB 0x1d12 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_0 0x2000 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_1 0x2002 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_2 0x2004 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_3 0x2006 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_4 0x2008 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_5 0x200a +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_6 0x200c +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_7 0x200e +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_8 0x2010 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_9 0x2012 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_10 0x2014 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_11 0x2016 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_12 0x2018 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_13 0x201a +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_14 0x201c +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_15 0x201e +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_0 0x2020 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_1 0x2022 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_2 0x2024 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_3 0x2026 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_4 0x2028 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_5 0x202a +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_6 0x202c +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_7 0x202e +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_8 0x2030 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_9 0x2032 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_10 0x2034 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_11 0x2036 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_12 0x2038 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_13 0x203a +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_14 0x203c +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_15 0x203e +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_0 0x2040 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_1 0x2042 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2 0x2044 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_3 0x2046 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_4 0x2048 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_5 0x204a +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_6 0x204c +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 0x204e +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_8 0x2050 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 0x2052 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_10 0x2054 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_11 0x2056 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_12 0x2058 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_13 0x205a +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_14 0x205c +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15 0x205e +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_0 0x2060 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_1 0x2062 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_2 0x2064 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_3 0x2066 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_4 0x2068 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_5 0x206a +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_6 0x206c +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_7 0x206e +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_8 0x2070 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_9 0x2072 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_10 0x2074 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_11 0x2076 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_12 0x2078 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_13 0x207a +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_14 0x207c +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_15 0x207e +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_0 0x2080 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_1 0x2082 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_2 0x2084 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_3 0x2086 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_4 0x2088 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_0 0x20a0 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_1 0x20a2 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_5_2 0x20a4 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_5_3 0x20a6 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_4 0x20a8 +#define IPU7_CORE_DIG_RW_TRIO0_0 0x2100 +#define IPU7_CORE_DIG_RW_TRIO0_1 0x2102 +#define IPU7_CORE_DIG_RW_TRIO0_2 0x2104 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_0 0x2400 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_1 0x2402 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_2 0x2404 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_3 0x2406 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_4 0x2408 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_5 0x240a +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_6 0x240c +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_7 0x240e +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_8 0x2410 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_9 0x2412 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_10 0x2414 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_11 0x2416 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_12 0x2418 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_13 0x241a +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_14 0x241c +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_15 0x241e +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_0 0x2420 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_1 0x2422 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_2 0x2424 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_3 0x2426 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_4 0x2428 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_5 0x242a +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_6 0x242c +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_7 0x242e +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_8 0x2430 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_9 0x2432 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_10 0x2434 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_11 0x2436 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_12 0x2438 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_13 0x243a +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_14 0x243c +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_15 0x243e +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_0 0x2440 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_1 0x2442 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2 0x2444 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_3 0x2446 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_4 0x2448 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_5 0x244a +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_6 0x244c +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_7 0x244e +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_8 0x2450 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9 0x2452 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_10 0x2454 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_11 0x2456 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_12 0x2458 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_13 0x245a +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_14 0x245c +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15 0x245e +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_0 0x2460 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_1 0x2462 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_2 0x2464 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_3 0x2466 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_4 0x2468 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_5 0x246a +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_6 0x246c +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_7 0x246e +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_8 0x2470 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_9 0x2472 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_10 0x2474 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_11 0x2476 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_12 0x2478 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_13 0x247a +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_14 0x247c +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_15 0x247e +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_0 0x2480 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_1 0x2482 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_2 0x2484 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_3 0x2486 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_4 0x2488 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_0 0x24a0 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_1 0x24a2 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_5_2 0x24a4 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_5_3 0x24a6 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_4 0x24a8 +#define IPU7_CORE_DIG_RW_TRIO1_0 0x2500 +#define IPU7_CORE_DIG_RW_TRIO1_1 0x2502 +#define IPU7_CORE_DIG_RW_TRIO1_2 0x2504 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_0 0x2800 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_1 0x2802 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_2 0x2804 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_3 0x2806 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_4 0x2808 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_5 0x280a +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_6 0x280c +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_7 0x280e +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_8 0x2810 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_9 0x2812 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_10 0x2814 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_11 0x2816 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_12 0x2818 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_13 0x281a +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_14 0x281c +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_15 0x281e +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_0 0x2820 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_1 0x2822 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_2 0x2824 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_3 0x2826 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_4 0x2828 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_5 0x282a +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_6 0x282c +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_7 0x282e +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_8 0x2830 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_9 0x2832 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_10 0x2834 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_11 0x2836 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_12 0x2838 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_13 0x283a +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_14 0x283c +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_15 0x283e +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_0 0x2840 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_1 0x2842 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2 0x2844 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_3 0x2846 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_4 0x2848 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_5 0x284a +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_6 0x284c +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_7 0x284e +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_8 0x2850 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9 0x2852 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_10 0x2854 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_11 0x2856 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_12 0x2858 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_13 0x285a +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_14 0x285c +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15 0x285e +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_0 0x2860 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_1 0x2862 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_2 0x2864 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_3 0x2866 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_4 0x2868 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_5 0x286a +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_6 0x286c +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_7 0x286e +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_8 0x2870 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_9 0x2872 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_10 0x2874 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_11 0x2876 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_12 0x2878 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_13 0x287a +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_14 0x287c +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_15 0x287e +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_0 0x2880 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_1 0x2882 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_2 0x2884 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_3 0x2886 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_4 0x2888 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_0 0x28a0 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_1 0x28a2 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_5_2 0x28a4 +#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_5_3 0x28a6 +#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_4 0x28a8 +#define IPU7_CORE_DIG_RW_TRIO2_0 0x2900 +#define IPU7_CORE_DIG_RW_TRIO2_1 0x2902 +#define IPU7_CORE_DIG_RW_TRIO2_2 0x2904 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_0 0x2c00 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_1 0x2c02 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_2 0x2c04 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_3 0x2c06 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_4 0x2c08 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_5 0x2c0a +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_6 0x2c0c +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_7 0x2c0e +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_8 0x2c10 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_9 0x2c12 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_10 0x2c14 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_11 0x2c16 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_12 0x2c18 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_13 0x2c1a +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_14 0x2c1c +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_15 0x2c1e +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_0 0x2c40 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_1 0x2c42 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2 0x2c44 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_3 0x2c46 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_4 0x2c48 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_5 0x2c4a +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_6 0x2c4c +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_7 0x2c4e +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_8 0x2c50 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9 0x2c52 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_10 0x2c54 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_11 0x2c56 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_12 0x2c58 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_13 0x2c5a +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_14 0x2c5c +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_15 0x2c5e +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_0 0x2c60 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_1 0x2c62 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_2 0x2c64 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_3 0x2c66 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_4 0x2c68 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_5 0x2c6a +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_6 0x2c6c +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_7 0x2c6e +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_8 0x2c70 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_9 0x2c72 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_10 0x2c74 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_11 0x2c76 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_12 0x2c78 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_13 0x2c7a +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_14 0x2c7c +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_15 0x2c7e +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_0 0x2c80 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_1 0x2c82 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_2 0x2c84 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_3 0x2c86 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_4 0x2c88 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_0 0x3040 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_1 0x3042 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2 0x3044 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_3 0x3046 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_4 0x3048 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_5 0x304a +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_6 0x304c +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_7 0x304e +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_8 0x3050 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9 0x3052 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_10 0x3054 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_11 0x3056 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_12 0x3058 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_13 0x305a +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_14 0x305c +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_15 0x305e +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_0 0x3060 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_1 0x3062 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_2 0x3064 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_3 0x3066 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_4 0x3068 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_5 0x306a +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_6 0x306c +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_7 0x306e +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_8 0x3070 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_9 0x3072 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_10 0x3074 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_11 0x3076 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_12 0x3078 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_13 0x307a +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_14 0x307c +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_15 0x307e +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_0 0x3080 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_1 0x3082 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_2 0x3084 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_3 0x3086 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_4 0x3088 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_CLK_OVR_0_0 0x3400 +#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_CLK_OVR_0_1 0x3402 +#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_CLK_OVR_0_2 0x3404 +#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_0 0x3800 +#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_1 0x3802 +#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_2 0x3804 +#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_3 0x3806 +#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_4 0x3808 +#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_5 0x380a +#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_6 0x380c +#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_7 0x380e +#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_8 0x3810 +#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_9 0x3812 +#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_10 0x3814 +#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_11 0x3816 +#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_12 0x3818 +#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_13 0x381a +#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_14 0x381c +#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_15 0x381e +#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_0 0x3820 +#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_1 0x3822 +#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_2 0x3824 +#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_3 0x3826 +#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_4 0x3828 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0 0x3840 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1 0x3842 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_2 0x3844 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_3 0x3846 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_4 0x3848 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5 0x384a +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_6 0x384c +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7 0x384e +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8 0x3850 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_9 0x3852 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_10 0x3854 +#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_11 0x3856 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_12 0x3858 +#define IPU7_CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_13 0x385a +#define IPU7_CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_14 0x385c +#define IPU7_CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_15 0x385e +#define IPU7_CORE_DIG_IOCTRL_R_AFE_CB_CTRL_3_0 0x3860 +#define IPU7_CORE_DIG_RW_COMMON_0 0x3880 +#define IPU7_CORE_DIG_RW_COMMON_1 0x3882 +#define IPU7_CORE_DIG_RW_COMMON_2 0x3884 +#define IPU7_CORE_DIG_RW_COMMON_3 0x3886 +#define IPU7_CORE_DIG_RW_COMMON_4 0x3888 +#define IPU7_CORE_DIG_RW_COMMON_5 0x388a +#define IPU7_CORE_DIG_RW_COMMON_6 0x388c +#define IPU7_CORE_DIG_RW_COMMON_7 0x388e +#define IPU7_CORE_DIG_RW_COMMON_8 0x3890 +#define IPU7_CORE_DIG_RW_COMMON_9 0x3892 +#define IPU7_CORE_DIG_RW_COMMON_10 0x3894 +#define IPU7_CORE_DIG_RW_COMMON_11 0x3896 +#define IPU7_CORE_DIG_RW_COMMON_12 0x3898 +#define IPU7_CORE_DIG_RW_COMMON_13 0x389a +#define IPU7_CORE_DIG_RW_COMMON_14 0x389c +#define IPU7_CORE_DIG_RW_COMMON_15 0x389e +#define IPU7_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_0 0x39e0 +#define IPU7_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_1 0x39e2 +#define IPU7_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_2 0x39e4 +#define IPU7_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_3 0x39e6 +#define IPU7_CORE_DIG_COMMON_RW_DESKEW_FINE_MEM 0x3fe0 +#define IPU7_CORE_DIG_COMMON_R_DESKEW_FINE_MEM 0x3fe2 +#define IPU7_PPI_RW_DPHY_LANE0_LBERT_0 0x4000 +#define IPU7_PPI_RW_DPHY_LANE0_LBERT_1 0x4002 +#define IPU7_PPI_R_DPHY_LANE0_LBERT_0 0x4004 +#define IPU7_PPI_R_DPHY_LANE0_LBERT_1 0x4006 +#define IPU7_PPI_RW_DPHY_LANE0_SPARE 0x4008 +#define IPU7_PPI_RW_DPHY_LANE1_LBERT_0 0x4400 +#define IPU7_PPI_RW_DPHY_LANE1_LBERT_1 0x4402 +#define IPU7_PPI_R_DPHY_LANE1_LBERT_0 0x4404 +#define IPU7_PPI_R_DPHY_LANE1_LBERT_1 0x4406 +#define IPU7_PPI_RW_DPHY_LANE1_SPARE 0x4408 +#define IPU7_PPI_RW_DPHY_LANE2_LBERT_0 0x4800 +#define IPU7_PPI_RW_DPHY_LANE2_LBERT_1 0x4802 +#define IPU7_PPI_R_DPHY_LANE2_LBERT_0 0x4804 +#define IPU7_PPI_R_DPHY_LANE2_LBERT_1 0x4806 +#define IPU7_PPI_RW_DPHY_LANE2_SPARE 0x4808 +#define IPU7_PPI_RW_DPHY_LANE3_LBERT_0 0x4c00 +#define IPU7_PPI_RW_DPHY_LANE3_LBERT_1 0x4c02 +#define IPU7_PPI_R_DPHY_LANE3_LBERT_0 0x4c04 +#define IPU7_PPI_R_DPHY_LANE3_LBERT_1 0x4c06 +#define IPU7_PPI_RW_DPHY_LANE3_SPARE 0x4c08 +#define IPU7_CORE_DIG_DLANE_0_RW_CFG_0 0x6000 +#define IPU7_CORE_DIG_DLANE_0_RW_CFG_1 0x6002 +#define IPU7_CORE_DIG_DLANE_0_RW_CFG_2 0x6004 +#define IPU7_CORE_DIG_DLANE_0_RW_LP_0 0x6080 +#define IPU7_CORE_DIG_DLANE_0_RW_LP_1 0x6082 +#define IPU7_CORE_DIG_DLANE_0_RW_LP_2 0x6084 +#define IPU7_CORE_DIG_DLANE_0_R_LP_0 0x60a0 +#define IPU7_CORE_DIG_DLANE_0_R_LP_1 0x60a2 +#define IPU7_CORE_DIG_DLANE_0_R_HS_TX_0 0x60e0 +#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_0 0x6100 +#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_1 0x6102 +#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_2 0x6104 +#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_3 0x6106 +#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_4 0x6108 +#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_5 0x610a +#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_6 0x610c +#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_7 0x610e +#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_8 0x6110 +#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_9 0x6112 +#define IPU7_CORE_DIG_DLANE_0_R_HS_RX_0 0x6120 +#define IPU7_CORE_DIG_DLANE_0_R_HS_RX_1 0x6122 +#define IPU7_CORE_DIG_DLANE_0_R_HS_RX_2 0x6124 +#define IPU7_CORE_DIG_DLANE_0_R_HS_RX_3 0x6126 +#define IPU7_CORE_DIG_DLANE_0_R_HS_RX_4 0x6128 +#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_0 0x6200 +#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_1 0x6202 +#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_2 0x6204 +#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_3 0x6206 +#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_4 0x6208 +#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_5 0x620a +#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_6 0x620c +#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_7 0x620e +#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_8 0x6210 +#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_9 0x6212 +#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_10 0x6214 +#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_11 0x6216 +#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_12 0x6218 +#define IPU7_CORE_DIG_DLANE_1_RW_CFG_0 0x6400 +#define IPU7_CORE_DIG_DLANE_1_RW_CFG_1 0x6402 +#define IPU7_CORE_DIG_DLANE_1_RW_CFG_2 0x6404 +#define IPU7_CORE_DIG_DLANE_1_RW_LP_0 0x6480 +#define IPU7_CORE_DIG_DLANE_1_RW_LP_1 0x6482 +#define IPU7_CORE_DIG_DLANE_1_RW_LP_2 0x6484 +#define IPU7_CORE_DIG_DLANE_1_R_LP_0 0x64a0 +#define IPU7_CORE_DIG_DLANE_1_R_LP_1 0x64a2 +#define IPU7_CORE_DIG_DLANE_1_R_HS_TX_0 0x64e0 +#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_0 0x6500 +#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_1 0x6502 +#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_2 0x6504 +#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_3 0x6506 +#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_4 0x6508 +#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_5 0x650a +#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_6 0x650c +#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_7 0x650e +#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_8 0x6510 +#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_9 0x6512 +#define IPU7_CORE_DIG_DLANE_1_R_HS_RX_0 0x6520 +#define IPU7_CORE_DIG_DLANE_1_R_HS_RX_1 0x6522 +#define IPU7_CORE_DIG_DLANE_1_R_HS_RX_2 0x6524 +#define IPU7_CORE_DIG_DLANE_1_R_HS_RX_3 0x6526 +#define IPU7_CORE_DIG_DLANE_1_R_HS_RX_4 0x6528 +#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_0 0x6600 +#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_1 0x6602 +#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_2 0x6604 +#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_3 0x6606 +#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_4 0x6608 +#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_5 0x660a +#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_6 0x660c +#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_7 0x660e +#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_8 0x6610 +#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_9 0x6612 +#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_10 0x6614 +#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_11 0x6616 +#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_12 0x6618 +#define IPU7_CORE_DIG_DLANE_2_RW_CFG_0 0x6800 +#define IPU7_CORE_DIG_DLANE_2_RW_CFG_1 0x6802 +#define IPU7_CORE_DIG_DLANE_2_RW_CFG_2 0x6804 +#define IPU7_CORE_DIG_DLANE_2_RW_LP_0 0x6880 +#define IPU7_CORE_DIG_DLANE_2_RW_LP_1 0x6882 +#define IPU7_CORE_DIG_DLANE_2_RW_LP_2 0x6884 +#define IPU7_CORE_DIG_DLANE_2_R_LP_0 0x68a0 +#define IPU7_CORE_DIG_DLANE_2_R_LP_1 0x68a2 +#define IPU7_CORE_DIG_DLANE_2_R_HS_TX_0 0x68e0 +#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_0 0x6900 +#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_1 0x6902 +#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_2 0x6904 +#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_3 0x6906 +#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_4 0x6908 +#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_5 0x690a +#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_6 0x690c +#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_7 0x690e +#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_8 0x6910 +#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_9 0x6912 +#define IPU7_CORE_DIG_DLANE_2_R_HS_RX_0 0x6920 +#define IPU7_CORE_DIG_DLANE_2_R_HS_RX_1 0x6922 +#define IPU7_CORE_DIG_DLANE_2_R_HS_RX_2 0x6924 +#define IPU7_CORE_DIG_DLANE_2_R_HS_RX_3 0x6926 +#define IPU7_CORE_DIG_DLANE_2_R_HS_RX_4 0x6928 +#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_0 0x6a00 +#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_1 0x6a02 +#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_2 0x6a04 +#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_3 0x6a06 +#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_4 0x6a08 +#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_5 0x6a0a +#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_6 0x6a0c +#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_7 0x6a0e +#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_8 0x6a10 +#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_9 0x6a12 +#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_10 0x6a14 +#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_11 0x6a16 +#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_12 0x6a18 +#define IPU7_CORE_DIG_DLANE_3_RW_CFG_0 0x6c00 +#define IPU7_CORE_DIG_DLANE_3_RW_CFG_1 0x6c02 +#define IPU7_CORE_DIG_DLANE_3_RW_CFG_2 0x6c04 +#define IPU7_CORE_DIG_DLANE_3_RW_LP_0 0x6c80 +#define IPU7_CORE_DIG_DLANE_3_RW_LP_1 0x6c82 +#define IPU7_CORE_DIG_DLANE_3_RW_LP_2 0x6c84 +#define IPU7_CORE_DIG_DLANE_3_R_LP_0 0x6ca0 +#define IPU7_CORE_DIG_DLANE_3_R_LP_1 0x6ca2 +#define IPU7_CORE_DIG_DLANE_3_R_HS_TX_0 0x6ce0 +#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_0 0x6d00 +#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_1 0x6d02 +#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_2 0x6d04 +#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_3 0x6d06 +#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_4 0x6d08 +#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_5 0x6d0a +#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_6 0x6d0c +#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_7 0x6d0e +#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_8 0x6d10 +#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_9 0x6d12 +#define IPU7_CORE_DIG_DLANE_3_R_HS_RX_0 0x6d20 +#define IPU7_CORE_DIG_DLANE_3_R_HS_RX_1 0x6d22 +#define IPU7_CORE_DIG_DLANE_3_R_HS_RX_2 0x6d24 +#define IPU7_CORE_DIG_DLANE_3_R_HS_RX_3 0x6d26 +#define IPU7_CORE_DIG_DLANE_3_R_HS_RX_4 0x6d28 +#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_0 0x6e00 +#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_1 0x6e02 +#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_2 0x6e04 +#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_3 0x6e06 +#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_4 0x6e08 +#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_5 0x6e0a +#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_6 0x6e0c +#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_7 0x6e0e +#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_8 0x6e10 +#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_9 0x6e12 +#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_10 0x6e14 +#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_11 0x6e16 +#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_12 0x6e18 +#define IPU7_CORE_DIG_DLANE_CLK_RW_CFG_0 0x7000 +#define IPU7_CORE_DIG_DLANE_CLK_RW_CFG_1 0x7002 +#define IPU7_CORE_DIG_DLANE_CLK_RW_CFG_2 0x7004 +#define IPU7_CORE_DIG_DLANE_CLK_RW_LP_0 0x7080 +#define IPU7_CORE_DIG_DLANE_CLK_RW_LP_1 0x7082 +#define IPU7_CORE_DIG_DLANE_CLK_RW_LP_2 0x7084 +#define IPU7_CORE_DIG_DLANE_CLK_R_LP_0 0x70a0 +#define IPU7_CORE_DIG_DLANE_CLK_R_LP_1 0x70a2 +#define IPU7_CORE_DIG_DLANE_CLK_R_HS_TX_0 0x70e0 +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_0 0x7100 +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_1 0x7102 +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_2 0x7104 +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_3 0x7106 +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_4 0x7108 +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_5 0x710a +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_6 0x710c +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_7 0x710e +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_8 0x7110 +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_9 0x7112 +#define IPU7_CORE_DIG_DLANE_CLK_R_HS_RX_0 0x7120 +#define IPU7_CORE_DIG_DLANE_CLK_R_HS_RX_1 0x7122 +#define IPU7_CORE_DIG_DLANE_CLK_R_HS_RX_2 0x7124 +#define IPU7_CORE_DIG_DLANE_CLK_R_HS_RX_3 0x7126 +#define IPU7_CORE_DIG_DLANE_CLK_R_HS_RX_4 0x7128 +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_0 0x7200 +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_1 0x7202 +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_2 0x7204 +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_3 0x7206 +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_4 0x7208 +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_5 0x720a +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_6 0x720c +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_7 0x720e +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_8 0x7210 +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_9 0x7212 +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_10 0x7214 +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_11 0x7216 +#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_12 0x7218 +#define IPU7_PPI_RW_CPHY_TRIO0_LBERT_0 0x8000 +#define IPU7_PPI_RW_CPHY_TRIO0_LBERT_1 0x8002 +#define IPU7_PPI_R_CPHY_TRIO0_LBERT_0 0x8004 +#define IPU7_PPI_R_CPHY_TRIO0_LBERT_1 0x8006 +#define IPU7_PPI_RW_CPHY_TRIO0_SPARE 0x8008 +#define IPU7_PPI_RW_CPHY_TRIO1_LBERT_0 0x8400 +#define IPU7_PPI_RW_CPHY_TRIO1_LBERT_1 0x8402 +#define IPU7_PPI_R_CPHY_TRIO1_LBERT_0 0x8404 +#define IPU7_PPI_R_CPHY_TRIO1_LBERT_1 0x8406 +#define IPU7_PPI_RW_CPHY_TRIO1_SPARE 0x8408 +#define IPU7_PPI_RW_CPHY_TRIO2_LBERT_0 0x8800 +#define IPU7_PPI_RW_CPHY_TRIO2_LBERT_1 0x8802 +#define IPU7_PPI_R_CPHY_TRIO2_LBERT_0 0x8804 +#define IPU7_PPI_R_CPHY_TRIO2_LBERT_1 0x8806 +#define IPU7_PPI_RW_CPHY_TRIO2_SPARE 0x8808 +#define IPU7_CORE_DIG_CLANE_0_RW_CFG_0 0xa000 +#define IPU7_CORE_DIG_CLANE_0_RW_CFG_2 0xa004 +#define IPU7_CORE_DIG_CLANE_0_RW_LP_0 0xa080 +#define IPU7_CORE_DIG_CLANE_0_RW_LP_1 0xa082 +#define IPU7_CORE_DIG_CLANE_0_RW_LP_2 0xa084 +#define IPU7_CORE_DIG_CLANE_0_R_LP_0 0xa0a0 +#define IPU7_CORE_DIG_CLANE_0_R_LP_1 0xa0a2 +#define IPU7_CORE_DIG_CLANE_0_RW_HS_RX_0 0xa100 +#define IPU7_CORE_DIG_CLANE_0_RW_HS_RX_1 0xa102 +#define IPU7_CORE_DIG_CLANE_0_RW_HS_RX_2 0xa104 +#define IPU7_CORE_DIG_CLANE_0_RW_HS_RX_3 0xa106 +#define IPU7_CORE_DIG_CLANE_0_RW_HS_RX_4 0xa108 +#define IPU7_CORE_DIG_CLANE_0_RW_HS_RX_5 0xa10a +#define IPU7_CORE_DIG_CLANE_0_RW_HS_RX_6 0xa10c +#define IPU7_CORE_DIG_CLANE_0_R_RX_0 0xa120 +#define IPU7_CORE_DIG_CLANE_0_R_RX_1 0xa122 +#define IPU7_CORE_DIG_CLANE_0_R_TX_0 0xa124 +#define IPU7_CORE_DIG_CLANE_0_R_RX_2 0xa126 +#define IPU7_CORE_DIG_CLANE_0_R_RX_3 0xa128 +#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_0 0xa200 +#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_1 0xa202 +#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_2 0xa204 +#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_3 0xa206 +#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_4 0xa208 +#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_5 0xa20a +#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_6 0xa20c +#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_7 0xa20e +#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_8 0xa210 +#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_9 0xa212 +#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_10 0xa214 +#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_11 0xa216 +#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_12 0xa218 +#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_13 0xa21a +#define IPU7_CORE_DIG_CLANE_1_RW_CFG_0 0xa400 +#define IPU7_CORE_DIG_CLANE_1_RW_CFG_2 0xa404 +#define IPU7_CORE_DIG_CLANE_1_RW_LP_0 0xa480 +#define IPU7_CORE_DIG_CLANE_1_RW_LP_1 0xa482 +#define IPU7_CORE_DIG_CLANE_1_RW_LP_2 0xa484 +#define IPU7_CORE_DIG_CLANE_1_R_LP_0 0xa4a0 +#define IPU7_CORE_DIG_CLANE_1_R_LP_1 0xa4a2 +#define IPU7_CORE_DIG_CLANE_1_RW_HS_RX_0 0xa500 +#define IPU7_CORE_DIG_CLANE_1_RW_HS_RX_1 0xa502 +#define IPU7_CORE_DIG_CLANE_1_RW_HS_RX_2 0xa504 +#define IPU7_CORE_DIG_CLANE_1_RW_HS_RX_3 0xa506 +#define IPU7_CORE_DIG_CLANE_1_RW_HS_RX_4 0xa508 +#define IPU7_CORE_DIG_CLANE_1_RW_HS_RX_5 0xa50a +#define IPU7_CORE_DIG_CLANE_1_RW_HS_RX_6 0xa50c +#define IPU7_CORE_DIG_CLANE_1_R_RX_0 0xa520 +#define IPU7_CORE_DIG_CLANE_1_R_RX_1 0xa522 +#define IPU7_CORE_DIG_CLANE_1_R_TX_0 0xa524 +#define IPU7_CORE_DIG_CLANE_1_R_RX_2 0xa526 +#define IPU7_CORE_DIG_CLANE_1_R_RX_3 0xa528 +#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_0 0xa600 +#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_1 0xa602 +#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_2 0xa604 +#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_3 0xa606 +#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_4 0xa608 +#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_5 0xa60a +#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_6 0xa60c +#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_7 0xa60e +#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_8 0xa610 +#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_9 0xa612 +#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_10 0xa614 +#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_11 0xa616 +#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_12 0xa618 +#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_13 0xa61a +#define IPU7_CORE_DIG_CLANE_2_RW_CFG_0 0xa800 +#define IPU7_CORE_DIG_CLANE_2_RW_CFG_2 0xa804 +#define IPU7_CORE_DIG_CLANE_2_RW_LP_0 0xa880 +#define IPU7_CORE_DIG_CLANE_2_RW_LP_1 0xa882 +#define IPU7_CORE_DIG_CLANE_2_RW_LP_2 0xa884 +#define IPU7_CORE_DIG_CLANE_2_R_LP_0 0xa8a0 +#define IPU7_CORE_DIG_CLANE_2_R_LP_1 0xa8a2 +#define IPU7_CORE_DIG_CLANE_2_RW_HS_RX_0 0xa900 +#define IPU7_CORE_DIG_CLANE_2_RW_HS_RX_1 0xa902 +#define IPU7_CORE_DIG_CLANE_2_RW_HS_RX_2 0xa904 +#define IPU7_CORE_DIG_CLANE_2_RW_HS_RX_3 0xa906 +#define IPU7_CORE_DIG_CLANE_2_RW_HS_RX_4 0xa908 +#define IPU7_CORE_DIG_CLANE_2_RW_HS_RX_5 0xa90a +#define IPU7_CORE_DIG_CLANE_2_RW_HS_RX_6 0xa90c +#define IPU7_CORE_DIG_CLANE_2_R_RX_0 0xa920 +#define IPU7_CORE_DIG_CLANE_2_R_RX_1 0xa922 +#define IPU7_CORE_DIG_CLANE_2_R_TX_0 0xa924 +#define IPU7_CORE_DIG_CLANE_2_R_RX_2 0xa926 +#define IPU7_CORE_DIG_CLANE_2_R_RX_3 0xa928 +#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_0 0xaa00 +#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_1 0xaa02 +#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_2 0xaa04 +#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_3 0xaa06 +#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_4 0xaa08 +#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_5 0xaa0a +#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_6 0xaa0c +#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_7 0xaa0e +#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_8 0xaa10 +#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_9 0xaa12 +#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_10 0xaa14 +#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_11 0xaa16 +#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_12 0xaa18 +#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_13 0xaa1a + +/* dwc csi host controller registers */ +#define IPU7_IS_IO_CSI2_HOST_BASE(i) (IPU7_IS_IO_BASE + 0x40000 + \ + 0x2000 * (i)) +#define IPU7_VERSION 0x0 +#define IPU7_N_LANES 0x4 +#define IPU7_CSI2_RESETN 0x8 +#define IPU7_INT_ST_MAIN 0xc +#define IPU7_DATA_IDS_1 0x10 +#define IPU7_DATA_IDS_2 0x14 +#define IPU7_CDPHY_MODE 0x1c +#define IPU7_DATA_IDS_VC_1 0x30 +#define IPU7_DATA_IDS_VC_2 0x34 +#define IPU7_PHY_SHUTDOWNZ 0x40 +#define IPU7_DPHY_RSTZ 0x44 +#define IPU7_PHY_RX 0x48 +#define IPU7_PHY_STOPSTATE 0x4c +#define IPU7_PHY_TEST_CTRL0 0x50 +#define IPU7_PHY_TEST_CTRL1 0x54 +#define IPU7_PPI_PG_PATTERN_VRES 0x60 +#define IPU7_PPI_PG_PATTERN_HRES 0x64 +#define IPU7_PPI_PG_CONFIG 0x68 +#define IPU7_PPI_PG_ENABLE 0x6c +#define IPU7_PPI_PG_STATUS 0x70 +#define IPU7_VC_EXTENSION 0xc8 +#define IPU7_PHY_CAL 0xcc +#define IPU7_INT_ST_PHY_FATAL 0xe0 +#define IPU7_INT_MSK_PHY_FATAL 0xe4 +#define IPU7_INT_FORCE_PHY_FATAL 0xe8 +#define IPU7_INT_ST_PKT_FATAL 0xf0 +#define IPU7_INT_MSK_PKT_FATAL 0xf4 +#define IPU7_INT_FORCE_PKT_FATAL 0xf8 +#define IPU7_INT_ST_PHY 0x110 +#define IPU7_INT_MSK_PHY 0x114 +#define IPU7_INT_FORCE_PHY 0x118 +#define IPU7_INT_ST_LINE 0x130 +#define IPU7_INT_MSK_LINE 0x134 +#define IPU7_INT_FORCE_LINE 0x138 +#define IPU7_INT_ST_BNDRY_FRAME_FATAL 0x280 +#define IPU7_INT_MSK_BNDRY_FRAME_FATAL 0x284 +#define IPU7_INT_FORCE_BNDRY_FRAME_FATAL 0x288 +#define IPU7_INT_ST_SEQ_FRAME_FATAL 0x290 +#define IPU7_INT_MSK_SEQ_FRAME_FATAL 0x294 +#define IPU7_INT_FORCE_SEQ_FRAME_FATAL 0x298 +#define IPU7_INT_ST_CRC_FRAME_FATAL 0x2a0 +#define IPU7_INT_MSK_CRC_FRAME_FATAL 0x2a4 +#define IPU7_INT_FORCE_CRC_FRAME_FATAL 0x2a8 +#define IPU7_INT_ST_PLD_CRC_FATAL 0x2b0 +#define IPU7_INT_MSK_PLD_CRC_FATAL 0x2b4 +#define IPU7_INT_FORCE_PLD_CRC_FATAL 0x2b8 +#define IPU7_INT_ST_DATA_ID 0x2c0 +#define IPU7_INT_MSK_DATA_ID 0x2c4 +#define IPU7_INT_FORCE_DATA_ID 0x2c8 +#define IPU7_INT_ST_ECC_CORRECTED 0x2d0 +#define IPU7_INT_MSK_ECC_CORRECTED 0x2d4 +#define IPU7_INT_FORCE_ECC_CORRECTED 0x2d8 +#define IPU7_SCRAMBLING 0x300 +#define IPU7_SCRAMBLING_SEED1 0x304 +#define IPU7_SCRAMBLING_SEED2 0x308 +#define IPU7_SCRAMBLING_SEED3 0x30c +#define IPU7_SCRAMBLING_SEED4 0x310 +#define IPU7_SCRAMBLING 0x300 + +#define IPU7_IS_IO_CSI2_ADPL_PORT_BASE(i) (IPU7_IS_IO_BASE + 0x40800 + \ + 0x2000 * (i)) +#define IPU7_CSI2_ADPL_INPUT_MODE 0x0 +#define IPU7_CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN 0x4 +#define IPU7_CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_ADDR 0x8 +#define IPU7_CSI2_ADPL_CSI_RX_ERR_IRQ_STATUS 0xc +#define IPU7_CSI2_ADPL_IRQ_CTL_COMMON_STATUS 0xa4 +#define IPU7_CSI2_ADPL_IRQ_CTL_COMMON_CLEAR 0xa8 +#define IPU7_CSI2_ADPL_IRQ_CTL_COMMON_ENABLE 0xac +#define IPU7_CSI2_ADPL_IRQ_CTL_FS_STATUS 0xbc +#define IPU7_CSI2_ADPL_IRQ_CTL_FS_CLEAR 0xc0 +#define IPU7_CSI2_ADPL_IRQ_CTL_FS_ENABLE 0xc4 +#define IPU7_CSI2_ADPL_IRQ_CTL_FE_STATUS 0xc8 +#define IPU7_CSI2_ADPL_IRQ_CTL_FE_CLEAR 0xcc +#define IPU7_CSI2_ADPL_IRQ_CTL_FE_ENABLE 0xd0 + +/* software control the legacy csi irq */ +#define IPU7_IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(i) (IPU7_IS_IO_BASE + \ + 0x40c00 + 0x2000 * (i)) +#define IPU7_IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(i) (IPU7_IS_IO_BASE + \ + 0x40d00 + 0x2000 * (i)) +#define IPU7_IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE (IPU7_IS_IO_BASE + \ + 0x49000) +#define IPU7_IS_IO_CSI2_IRQ_CTRL_BASE (IPU7_IS_IO_BASE + 0x4e100) + +#define IPU7_IRQ_CTL_EDGE 0x0 +#define IPU7_IRQ_CTL_MASK 0x4 +#define IPU7_IRQ_CTL_STATUS 0x8 +#define IPU7_IRQ_CTL_CLEAR 0xc +#define IPU7_IRQ_CTL_ENABLE 0x10 +/* FE irq for PTL */ +#define IPU7_IRQ1_CTL_MASK 0x14 +#define IPU7_IRQ1_CTL_STATUS 0x18 +#define IPU7_IRQ1_CTL_CLEAR 0x1c +#define IPU7_IRQ1_CTL_ENABLE 0x20 + +/* software to set the clock gate to use the port or mgc */ +#define IPU7_IS_IO_GPREGS_BASE (IPU7_IS_IO_BASE + 0x49200) +#define IPU7_SRST_PORT_ARB 0x0 +#define IPU7_SRST_MGC 0x4 +#define IPU7_SRST_WIDTH_CONV 0x8 +#define IPU7_SRST_CSI_IRQ 0xc +#define IPU7_SRST_CSI_LEGACY_IRQ 0x10 +#define IPU7_CLK_EN_TXCLKESC 0x14 +#define IPU7_CLK_DIV_FACTOR_IS_CLK 0x18 +#define IPU7_CLK_DIV_FACTOR_APB_CLK 0x1c +#define IPU7_CSI_PORT_CLK_GATE 0x20 +#define IPU7_CSI_PORTAB_AGGREGATION 0x24 +#define IPU7_MGC_CLK_GATE 0x2c +#define IPU7_CG_CTRL_BITS 0x3c +#define IPU7_SPARE_RW 0xf8 +#define IPU7_SPARE_RO 0xfc + +#define IPU7_IS_IO_CSI2_MPF_PORT_BASE(i) (IPU7_IS_IO_BASE + 0x53000 + \ + 0x1000 * (i)) +#define IPU7_MPF_16_IRQ_CNTRL_STATUS 0x238 +#define IPU7_MPF_16_IRQ_CNTRL_CLEAR 0x23c +#define IPU7_MPF_16_IRQ_CNTRL_ENABLE 0x240 + +/* software config the phy */ +#define IPU7_IS_IO_CSI2_GPREGS_BASE (IPU7_IS_IO_BASE + 0x53400) +#define IPU8_IS_IO_CSI2_GPREGS_BASE (IPU7_IS_IO_BASE + 0x40e00) +#define IPU7_CSI_ADAPT_LAYER_SRST 0x0 +#define IPU7_MPF_SRST_RST 0x4 +#define IPU7_CSI_ERR_IRQ_CTRL_SRST 0x8 +#define IPU7_CSI_SYNC_RC_SRST 0xc +#define IPU7_CSI_CG_CTRL_BITS 0x10 +#define IPU7_SOC_CSI2HOST_SELECT 0x14 +#define IPU7_PHY_RESET 0x18 +#define IPU7_PHY_SHUTDOWN 0x1c +#define IPU7_PHY_MODE 0x20 +#define IPU7_PHY_READY 0x24 +#define IPU7_PHY_CLK_LANE_FORCE_CONTROL 0x28 +#define IPU7_PHY_CLK_LANE_CONTROL 0x2c +#define IPU7_PHY_CLK_LANE_STATUS 0x30 +#define IPU7_PHY_LANE_RX_ESC_REQ 0x34 +#define IPU7_PHY_LANE_RX_ESC_DATA 0x38 +#define IPU7_PHY_LANE_TURNDISABLE 0x3c +#define IPU7_PHY_LANE_DIRECTION 0x40 +#define IPU7_PHY_LANE_FORCE_CONTROL 0x44 +#define IPU7_PHY_LANE_CONTROL_EN 0x48 +#define IPU7_PHY_LANE_CONTROL_DATAWIDTH 0x4c +#define IPU7_PHY_LANE_STATUS 0x50 +#define IPU7_PHY_LANE_ERR 0x54 +#define IPU7_PHY_LANE_RXALP 0x58 +#define IPU7_PHY_LANE_RXALP_NIBBLE 0x5c +#define IPU7_PHY_PARITY_ERROR 0x60 +#define IPU7_PHY_DEBUG_REGS_CLK_GATE_EN 0x64 +#define IPU7_SPARE_RW 0xf8 +#define IPU7_SPARE_RO 0xfc + +/* software not touch */ +#define IPU7_PORT_ARB_BASE (IPU7_IS_IO_BASE + 0x4e000) +#define IPU7_PORT_ARB_IRQ_CTL_STATUS 0x4 +#define IPU7_PORT_ARB_IRQ_CTL_CLEAR 0x8 +#define IPU7_PORT_ARB_IRQ_CTL_ENABLE 0xc + +#define IPU7_MGC_PPC 4U +#define IPU7_MGC_DTYPE_RAW(i) (((i) - 8) / 2) +#define IPU7_IS_IO_MGC_BASE (IPU7_IS_IO_BASE + 0x48000) +#define IPU7_MGC_KICK 0x0 +#define IPU7_MGC_ASYNC_STOP 0x4 +#define IPU7_MGC_PORT_OFFSET 0x100 +#define IPU7_MGC_CSI_PORT_MAP(i) (0x8 + (i) * 0x4) +#define IPU7_MGC_MG_PORT(i) (IPU7_IS_IO_MGC_BASE + \ + (i) * IPU7_MGC_PORT_OFFSET) +/* per mgc instance */ +#define IPU7_MGC_MG_CSI_ADAPT_LAYER_TYPE 0x28 +#define IPU7_MGC_MG_MODE 0x2c +#define IPU7_MGC_MG_INIT_COUNTER 0x30 +#define IPU7_MGC_MG_MIPI_VC 0x34 +#define IPU7_MGC_MG_MIPI_DTYPES 0x38 +#define IPU7_MGC_MG_MULTI_DTYPES_MODE 0x3c +#define IPU7_MGC_MG_NOF_FRAMES 0x40 +#define IPU7_MGC_MG_FRAME_DIM 0x44 +#define IPU7_MGC_MG_HBLANK 0x48 +#define IPU7_MGC_MG_VBLANK 0x4c +#define IPU7_MGC_MG_TPG_MODE 0x50 +#define IPU7_MGC_MG_TPG_R0 0x54 +#define IPU7_MGC_MG_TPG_G0 0x58 +#define IPU7_MGC_MG_TPG_B0 0x5c +#define IPU7_MGC_MG_TPG_R1 0x60 +#define IPU7_MGC_MG_TPG_G1 0x64 +#define IPU7_MGC_MG_TPG_B1 0x68 +#define IPU7_MGC_MG_TPG_FACTORS 0x6c +#define IPU7_MGC_MG_TPG_MASKS 0x70 +#define IPU7_MGC_MG_TPG_XY_MASK 0x74 +#define IPU7_MGC_MG_TPG_TILE_DIM 0x78 +#define IPU7_MGC_MG_PRBS_LFSR_INIT_0 0x7c +#define IPU7_MGC_MG_PRBS_LFSR_INIT_1 0x80 +#define IPU7_MGC_MG_SYNC_STOP_POINT 0x84 +#define IPU7_MGC_MG_SYNC_STOP_POINT_LOC 0x88 +#define IPU7_MGC_MG_ERR_INJECT 0x8c +#define IPU7_MGC_MG_ERR_LOCATION 0x90 +#define IPU7_MGC_MG_DTO_SPEED_CTRL_EN 0x94 +#define IPU7_MGC_MG_DTO_SPEED_CTRL_INCR_VAL 0x98 +#define IPU7_MGC_MG_HOR_LOC_STTS 0x9c +#define IPU7_MGC_MG_VER_LOC_STTS 0xa0 +#define IPU7_MGC_MG_FRAME_NUM_STTS 0xa4 +#define IPU7_MGC_MG_BUSY_STTS 0xa8 +#define IPU7_MGC_MG_STOPPED_STTS 0xac +/* tile width and height in pixels for Chess board and Color palette */ +#define IPU7_MGC_TPG_TILE_WIDTH 64U +#define IPU7_MGC_TPG_TILE_HEIGHT 64U + +#define IPU7_CSI_PORT_A_ADDR_OFFSET 0x0 +#define IPU7_CSI_PORT_B_ADDR_OFFSET 0x0 +#define IPU7_CSI_PORT_C_ADDR_OFFSET 0x0 +#define IPU7_CSI_PORT_D_ADDR_OFFSET 0x0 + +/* + * 0 - CSI RX Port 0 interrupt; + * 1 - MPF Port 0 interrupt; + * 2 - CSI RX Port 1 interrupt; + * 3 - MPF Port 1 interrupt; + * 4 - CSI RX Port 2 interrupt; + * 5 - MPF Port 2 interrupt; + * 6 - CSI RX Port 3 interrupt; + * 7 - MPF Port 3 interrupt; + * 8 - Port ARB FIFO 0 overflow; + * 9 - Port ARB FIFO 1 overflow; + * 10 - Port ARB FIFO 2 overflow; + * 11 - Port ARB FIFO 3 overflow; + * 12 - isys_cfgnoc_err_probe_intl; + * 13-15 - reserved + */ +#define IPU7_CSI_IS_IO_IRQ_MASK 0xffff + +/* Adapter layer irq */ +#define IPU7_CSI_ADPL_IRQ_MASK 0xffff + +/* sw irq from legacy irq control + * legacy irq status + * IPU7 + * 0 - CSI Port 0 error interrupt + * 1 - CSI Port 0 sync interrupt + * 2 - CSI Port 1 error interrupt + * 3 - CSI Port 1 sync interrupt + * 4 - CSI Port 2 error interrupt + * 5 - CSI Port 2 sync interrupt + * 6 - CSI Port 3 error interrupt + * 7 - CSI Port 3 sync interrupt + * IPU7P5 + * 0 - CSI Port 0 error interrupt + * 1 - CSI Port 0 fs interrupt + * 2 - CSI Port 0 fe interrupt + * 3 - CSI Port 1 error interrupt + * 4 - CSI Port 1 fs interrupt + * 5 - CSI Port 1 fe interrupt + * 6 - CSI Port 2 error interrupt + * 7 - CSI Port 2 fs interrupt + * 8 - CSI Port 2 fe interrupt + */ +#define IPU7_CSI_RX_LEGACY_IRQ_MASK 0x1ff + +/* legacy error status per port + * 0 - Error handler FIFO full; + * 1 - Reserved Short Packet encoding detected; + * 2 - Reserved Long Packet encoding detected; + * 3 - Received packet is too short (fewer data words than specified in header); + * 4 - Received packet is too long (more data words than specified in header); + * 5 - Short packet discarded due to errors; + * 6 - Long packet discarded due to errors; + * 7 - CSI Combo Rx interrupt; + * 8 - IDI CDC FIFO overflow; remaining bits are reserved and tied to 0; + */ +#define IPU7_CSI_RX_ERROR_IRQ_MASK 0xfff + +/* + * 0 - VC0 frame start received + * 1 - VC0 frame end received + * 2 - VC1 frame start received + * 3 - VC1 frame end received + * 4 - VC2 frame start received + * 5 - VC2 frame end received + * 6 - VC3 frame start received + * 7 - VC3 frame end received + * 8 - VC4 frame start received + * 9 - VC4 frame end received + * 10 - VC5 frame start received + * 11 - VC5 frame end received + * 12 - VC6 frame start received + * 13 - VC6 frame end received + * 14 - VC7 frame start received + * 15 - VC7 frame end received + * 16 - VC8 frame start received + * 17 - VC8 frame end received + * 18 - VC9 frame start received + * 19 - VC9 frame end received + * 20 - VC10 frame start received + * 21 - VC10 frame end received + * 22 - VC11 frame start received + * 23 - VC11 frame end received + * 24 - VC12 frame start received + * 25 - VC12 frame end received + * 26 - VC13 frame start received + * 27 - VC13 frame end received + * 28 - VC14 frame start received + * 29 - VC14 frame end received + * 30 - VC15 frame start received + * 31 - VC15 frame end received + */ + +#define IPU7_CSI_RX_SYNC_IRQ_MASK 0x0 +#define IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK 0x0 + +#define IPU7_CSI_RX_NUM_ERRORS_IN_IRQ 12U +#define IPU7_CSI_RX_NUM_SYNC_IN_IRQ 32U + +enum IPU7_MGC_CSI_ADPL_TYPE { + IPU7_MGC_MAPPED_2_LANES = 0, + IPU7_MGC_MAPPED_4_LANES = 1, +}; + +enum IPU7_CSI2HOST_SELECTION { + IPU7_CSI2HOST_SEL_SOC = 0, + IPU7_CSI2HOST_SEL_CSI2HOST = 1, +}; + +#define IPU7_CSI_LEGACY_IRQ_MASK(port) (0x3 << ((port) * 2)) +#define IPU7P5_CSI_LEGACY_IRQ_MASK(port) (0x7 << ((port) * 3)) + +#define IPU7_ISYS_LEGACY_IRQ_CSI2(port) (0x3 << (port)) +#define IPU7P5_ISYS_LEGACY_IRQ_CSI2(port) (0x7 << (port)) + +/* ---------------------------------------------------------------- */ +#define IPU7_CSI_REG_BASE 0x220000 +#define IPU7_CSI_REG_BASE_PORT(id) ((id) * 0x1000) + +/* CSI Port General Purpose Registers */ +#define IPU7_CSI_REG_PORT_GPREG_SRST 0x0 +#define IPU7_CSI_REG_PORT_GPREG_CSI2_SLV_REG_SRST 0x4 +#define IPU7_CSI_REG_PORT_GPREG_CSI2_PORT_CONTROL 0x8 + +#define IPU7_CSI_RX_SYNC_FS_VC 0x55555555 +#define IPU7_CSI_RX_SYNC_FE_VC 0xaaaaaaaa +#define IPU7P5_CSI_RX_SYNC_FS_VC 0xffff +#define IPU7P5_CSI_RX_SYNC_FE_VC 0xffff + +#endif /* IPU7_ISYS_CSI2_REG_H */ diff --git a/drivers/media/pci/intel/ipu6/ipu7-mmu-hw.c b/drivers/media/pci/intel/ipu6/ipu7-mmu-hw.c new file mode 100644 index 000000000000..c9b015e89ee6 --- /dev/null +++ b/drivers/media/pci/intel/ipu6/ipu7-mmu-hw.c @@ -0,0 +1,858 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Intel Corporation + */ + +#include <linux/types.h> +#include <linux/iopoll.h> + +#include "ipu6.h" +#include "ipu6-dma.h" +#include "ipu6-mmu.h" + +static struct ipu7_mmu_hw ipu7_isys_mmu_hwdata[] = { + { + .offset = IPU7_IS_MMU_FW_RD_OFFSET, + .zlx_offset = IPU7_IS_ZLX_UC_RD_OFFSET, + .uao_offset = IPU7_IS_UAO_UC_RD_OFFSET, + .info_bits = 0x20006701, + .refill = 0x00002726, + .collapse_en_bitmap = 0x0, + .l1_block = IPU7_IS_MMU_FW_RD_L1_BLOCKNR_REG, + .l2_block = IPU7_IS_MMU_FW_RD_L2_BLOCKNR_REG, + .nr_l1streams = IPU7_IS_MMU_FW_RD_STREAM_NUM, + .nr_l2streams = IPU7_IS_MMU_FW_RD_STREAM_NUM, + .l1_block_sz = { 0x0, 0x8, 0xa }, + .l2_block_sz = { 0x0, 0x2, 0x4 }, + .zlx_nr = IPU7_IS_ZLX_UC_RD_NUM, + .zlx_axi_pool = { 0x00000f30 }, + .zlx_en = { 0, 0, 0, 0 }, + .zlx_conf = { 0x0, 0x0, 0x0, 0x0 }, + .uao_p_num = IPU7_IS_UAO_UC_RD_PLANENUM, + .uao_p2tlb = { 0x61, 0x64, 0x65 }, + }, + { + .offset = IPU7_IS_MMU_FW_WR_OFFSET, + .zlx_offset = IPU7_IS_ZLX_UC_WR_OFFSET, + .uao_offset = IPU7_IS_UAO_UC_WR_OFFSET, + .info_bits = 0x20006801, + .refill = 0x00002524, + .collapse_en_bitmap = 0x0, + .l1_block = IPU7_IS_MMU_FW_WR_L1_BLOCKNR_REG, + .l2_block = IPU7_IS_MMU_FW_WR_L2_BLOCKNR_REG, + .nr_l1streams = IPU7_IS_MMU_FW_WR_STREAM_NUM, + .nr_l2streams = IPU7_IS_MMU_FW_WR_STREAM_NUM, + .l1_block_sz = { 0x0, 0x8, 0xa }, + .l2_block_sz = { 0x0, 0x2, 0x4 }, + .zlx_nr = IPU7_IS_ZLX_UC_WR_NUM, + .zlx_axi_pool = { 0x00000f20 }, + .zlx_en = { 0, 1, 1, 0 }, + .zlx_conf = { 0x0, 0x00010101, 0x00010101 }, + .uao_p_num = IPU7_IS_UAO_UC_WR_PLANENUM, + .uao_p2tlb = { 0x61, 0x62, 0x63 }, + }, + { + .offset = IPU7_IS_MMU_M0_OFFSET, + .zlx_offset = IPU7_IS_ZLX_M0_OFFSET, + .uao_offset = IPU7_IS_UAO_M0_WR_OFFSET, + .info_bits = 0x20006601, + .refill = 0x00002120, + .collapse_en_bitmap = 0x0, + .l1_block = IPU7_IS_MMU_M0_L1_BLOCKNR_REG, + .l2_block = IPU7_IS_MMU_M0_L2_BLOCKNR_REG, + .nr_l1streams = IPU7_IS_MMU_M0_STREAM_NUM, + .nr_l2streams = IPU7_IS_MMU_M0_STREAM_NUM, + .l1_block_sz = { 0x0, 0x3, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10 }, + .l2_block_sz = { 0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe }, + .zlx_nr = IPU7_IS_ZLX_M0_NUM, + .zlx_axi_pool = { 0x00000f10 }, + .zlx_en = { 1, 1, 1, 1, 1, 1, 1, 1 }, + .zlx_conf = { + 0x00010103, + 0x00010103, + 0x00010101, + 0x00010101, + 0x00010101, + 0x00010101, + 0x00010101, + 0x00010101, + }, + .uao_p_num = IPU7_IS_UAO_M0_WR_PLANENUM, + .uao_p2tlb = { + 0x00000049, + 0x0000004a, + 0x0000004b, + 0x0000004c, + 0x0000004d, + 0x0000004e, + 0x0000004f, + 0x00000050, + }, + }, + { + .offset = IPU7_IS_MMU_M1_OFFSET, + .zlx_offset = IPU7_IS_ZLX_M1_OFFSET, + .uao_offset = IPU7_IS_UAO_M1_WR_OFFSET, + .info_bits = 0x20006901, + .refill = 0x00002322, + .collapse_en_bitmap = 0x0, + .l1_block = IPU7_IS_MMU_M1_L1_BLOCKNR_REG, + .l2_block = IPU7_IS_MMU_M1_L2_BLOCKNR_REG, + .nr_l1streams = IPU7_IS_MMU_M1_STREAM_NUM, + .nr_l2streams = IPU7_IS_MMU_M1_STREAM_NUM, + .l1_block_sz = { + 0x0, 0x3, 0x6, 0x9, 0xc, + 0xe, 0x10, 0x12, 0x14, 0x16, + 0x18, 0x1a, 0x1c, 0x1e, 0x20, 0x22, + }, + .l2_block_sz = { + 0x0, 0x2, 0x4, 0x6, 0x8, + 0xa, 0xc, 0xe, 0x10, 0x12, + 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, + }, + .zlx_nr = IPU7_IS_ZLX_M1_NUM, + .zlx_axi_pool = { 0x00000f20 }, + .zlx_en = { 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + }, + .zlx_conf = { + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010101, + 0x00010101, + 0x00010101, + 0x00010101, + 0x00010101, + 0x00010101, + 0x00010101, + 0x00010101, + }, + .uao_p_num = IPU7_IS_UAO_M1_WR_PLANENUM, + .uao_p2tlb = { + 0x00000051, + 0x00000052, + 0x00000053, + 0x00000054, + 0x00000055, + 0x00000056, + 0x00000057, + 0x00000058, + 0x00000059, + 0x0000005a, + 0x0000005b, + 0x0000005c, + 0x0000005d, + 0x0000005e, + 0x0000005f, + 0x00000060, + }, + }, +}; + +static struct ipu7_mmu_hw ipu7_psys_mmu_hwdata[] = { + { + .name = "PS_FW_RD", + .offset = IPU7_PS_MMU_FW_RD_OFFSET, + .zlx_offset = IPU7_PS_ZLX_FW_RD_OFFSET, + .uao_offset = IPU7_PS_UAO_FW_RD_OFFSET, + .info_bits = 0x20004801, + .refill = 0x00002726, + .collapse_en_bitmap = 0x0, + .l1_block = IPU7_PS_MMU_FW_RD_L1_BLOCKNR_REG, + .l2_block = IPU7_PS_MMU_FW_RD_L2_BLOCKNR_REG, + .nr_l1streams = IPU7_PS_MMU_FW_RD_STREAM_NUM, + .nr_l2streams = IPU7_PS_MMU_FW_RD_STREAM_NUM, + .l1_block_sz = { + 0, 0x8, 0xa, 0xc, 0xd, + 0xf, 0x11, 0x12, 0x13, 0x14, + 0x16, 0x18, 0x19, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + }, + .l2_block_sz = { + 0x0, 0x2, 0x4, 0x6, 0x8, + 0xa, 0xc, 0xe, 0x10, 0x12, + 0x14, 0x16, 0x18, 0x1a, 0x1c, + 0x1e, 0x20, 0x22, 0x24, 0x26, + }, + .zlx_nr = IPU7_PS_ZLX_FW_RD_NUM, + .zlx_axi_pool = { 0x00000f30 }, + .zlx_en = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + .zlx_conf = { 0x0 }, + .uao_p_num = IPU7_PS_UAO_FW_RD_PLANENUM, + .uao_p2tlb = { + 0x00000036, + 0x0000003d, + 0x0000003e, + 0x00000039, + 0x0000003f, + 0x00000040, + 0x00000041, + 0x0000003a, + 0x0000003b, + 0x00000042, + 0x00000043, + 0x00000044, + 0x0000003c, + }, + }, + { + .offset = IPU7_PS_MMU_FW_WR_OFFSET, + .zlx_offset = IPU7_PS_ZLX_FW_WR_OFFSET, + .uao_offset = IPU7_PS_UAO_FW_WR_OFFSET, + .info_bits = 0x20004601, + .refill = 0x00002322, + .collapse_en_bitmap = 0x0, + .l1_block = IPU7_PS_MMU_FW_WR_L1_BLOCKNR_REG, + .l2_block = IPU7_PS_MMU_FW_WR_L2_BLOCKNR_REG, + .nr_l1streams = IPU7_PS_MMU_FW_WR_STREAM_NUM, + .nr_l2streams = IPU7_PS_MMU_FW_WR_STREAM_NUM, + .l1_block_sz = { + 0, 0x8, 0xa, 0xc, 0xd, + 0xe, 0xf, 0x10, 0x10, 0x10, + }, + .l2_block_sz = { + 0x0, 0x2, 0x4, 0x6, 0x8, + 0xa, 0xc, 0xe, 0x10, 0x12, + }, + .zlx_nr = IPU7_PS_ZLX_FW_WR_NUM, + .zlx_axi_pool = { 0x00000f20 }, + .zlx_en = { 0, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, + .zlx_conf = { 0x0, 0x00010101, 0x00010101 }, + .uao_p_num = IPU7_PS_UAO_FW_WR_PLANENUM, + .uao_p2tlb = { 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c }, + }, + { + .offset = IPU7_PS_MMU_SRT_RD_OFFSET, + .zlx_offset = IPU7_PS_ZLX_DATA_RD_OFFSET, + .uao_offset = IPU7_PS_UAO_SRT_RD_OFFSET, + .info_bits = 0x20004701, + .refill = 0x00002120, + .collapse_en_bitmap = 0x0, + .l1_block = IPU7_PS_MMU_SRT_RD_L1_BLOCKNR_REG, + .l2_block = IPU7_PS_MMU_SRT_RD_L2_BLOCKNR_REG, + .nr_l1streams = IPU7_PS_MMU_SRT_RD_STREAM_NUM, + .nr_l2streams = IPU7_PS_MMU_SRT_RD_STREAM_NUM, + .l1_block_sz = { + 0x0, 0x4, 0x6, 0x8, 0xb, + 0xd, 0xf, 0x11, 0x13, 0x15, + 0x17, 0x23, 0x2b, 0x37, 0x3f, + 0x41, 0x43, 0x44, 0x45, 0x46, + 0x47, 0x48, 0x49, 0x4a, 0x4b, + 0x4c, 0x4d, 0x4e, 0x4f, 0x50, + 0x51, 0x52, 0x53, 0x55, 0x57, + 0x59, 0x5b, 0x5d, 0x5f, 0x61, + }, + .l2_block_sz = { + 0x0, 0x2, 0x4, 0x6, 0x8, + 0xa, 0xc, 0xe, 0x10, 0x12, + 0x14, 0x16, 0x18, 0x1a, 0x1c, + 0x1e, 0x20, 0x22, 0x24, 0x26, + 0x28, 0x2a, 0x2c, 0x2e, 0x30, + 0x32, 0x34, 0x36, 0x38, 0x3a, + 0x3c, 0x3e, 0x40, 0x42, 0x44, + 0x46, 0x48, 0x4a, 0x4c, 0x4e, + }, + .zlx_nr = IPU7_PS_ZLX_DATA_RD_NUM, + .zlx_axi_pool = { 0x00000f30 }, + .zlx_en = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + .zlx_conf = { + 0x00030303, + 0x00010101, + 0x00010101, + 0x00030202, + 0x00010101, + 0x00010101, + 0x00010101, + 0x00030800, + 0x00030500, + 0x00020101, + 0x00042000, + 0x00031000, + 0x00042000, + 0x00031000, + 0x00020400, + 0x00010101, + }, + .uao_p_num = IPU7_PS_UAO_SRT_RD_PLANENUM, + .uao_p2tlb = { + 0x00000022, + 0x00000023, + 0x00000024, + 0x00000025, + 0x00000026, + 0x00000027, + 0x00000028, + 0x00000029, + 0x0000002a, + 0x0000002b, + 0x0000002c, + 0x0000002d, + 0x0000002e, + 0x0000002f, + 0x00000030, + 0x00000031, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0000001e, + 0x0000001f, + 0x00000020, + 0x00000021, + 0x00000032, + 0x00000033, + 0x00000034, + 0x00000035, + }, + }, + { + .offset = IPU7_PS_MMU_SRT_WR_OFFSET, + .zlx_offset = IPU7_PS_ZLX_DATA_WR_OFFSET, + .uao_offset = IPU7_PS_UAO_SRT_WR_OFFSET, + .info_bits = 0x20004501, + .refill = 0x00002120, + .collapse_en_bitmap = 0x0, + .l1_block = IPU7_PS_MMU_SRT_WR_L1_BLOCKNR_REG, + .l2_block = IPU7_PS_MMU_SRT_WR_L2_BLOCKNR_REG, + .nr_l1streams = IPU7_PS_MMU_SRT_WR_STREAM_NUM, + .nr_l2streams = IPU7_PS_MMU_SRT_WR_STREAM_NUM, + .l1_block_sz = { + 0x0, 0x2, 0x6, 0xa, 0xc, + 0xe, 0x10, 0x12, 0x14, 0x16, + 0x18, 0x1a, 0x1c, 0x1e, 0x20, + 0x22, 0x24, 0x26, 0x32, 0x3a, + 0x3c, 0x3e, 0x4a, 0x52, 0x58, + 0x64, 0x6c, 0x72, 0x7e, 0x86, + 0x8c, 0x8d, 0x8e, 0x8f, 0x90, + 0x91, 0x92, 0x94, 0x96, 0x98, + }, + .l2_block_sz = { + 0x0, 0x2, 0x4, 0x6, 0x8, + 0xa, 0xc, 0xe, 0x10, 0x12, + 0x14, 0x16, 0x18, 0x1a, 0x1c, + 0x1e, 0x20, 0x22, 0x24, 0x26, + 0x28, 0x2a, 0x2c, 0x2e, 0x30, + 0x32, 0x34, 0x36, 0x38, 0x3a, + 0x3c, 0x3e, 0x40, 0x42, 0x44, + 0x46, 0x48, 0x4a, 0x4c, 0x4e, + }, + .zlx_nr = IPU7_PS_ZLX_DATA_WR_NUM, + .zlx_axi_pool = { 0x00000f50 }, + .zlx_en = { + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 0, + }, + .zlx_conf = { + 0x00010102, + 0x00030103, + 0x00030103, + 0x00010101, + 0x00010101, + 0x00030101, + 0x00010101, + 0x38010101, + 0x0, + 0x0, + 0x38010101, + 0x38010101, + 0x38010101, + 0x38010101, + 0x38010101, + 0x38010101, + 0x00010101, + 0x00042000, + 0x00031000, + 0x00010101, + 0x00010101, + 0x00042000, + 0x00031000, + 0x00031000, + 0x00042000, + 0x00031000, + 0x00031000, + 0x00042000, + 0x00031000, + 0x00031000, + 0x0, + 0x0, + }, + .uao_p_num = IPU7_PS_UAO_SRT_WR_PLANENUM, + .uao_p2tlb = { + 0x00000000, + 0x00000001, + 0x00000002, + 0x00000003, + 0x00000004, + 0x00000005, + 0x00000006, + 0x00000007, + 0x00000008, + 0x00000009, + 0x0000000a, + 0x0000000b, + 0x0000000c, + 0x0000000d, + 0x0000000e, + 0x0000000f, + 0x00000010, + 0x00000011, + 0x00000012, + 0x00000013, + 0x00000014, + 0x00000015, + 0x00000016, + 0x00000017, + 0x00000018, + 0x00000019, + 0x0000001a, + 0x0000001b, + 0x0000001c, + 0x0000001d, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0000001e, + 0x0000001f, + 0x00000020, + 0x00000021, + }, + }, +}; + +static struct ipu7_mmu_hw ipu7p5_isys_mmu_hwdata[] = { + { + .name = "IS_FW_RD", + .offset = IPU7P5_IS_MMU_FW_RD_OFFSET, + .zlx_offset = IPU7P5_IS_ZLX_UC_RD_OFFSET, + .uao_offset = IPU7P5_IS_UAO_UC_RD_OFFSET, + .info_bits = 0x20005101, + .refill = 0x00002726, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU7P5_IS_MMU_FW_RD_L1_BLOCKNR_REG, + .l2_block = IPU7P5_IS_MMU_FW_RD_L2_BLOCKNR_REG, + .nr_l1streams = IPU7P5_IS_MMU_FW_RD_STREAM_NUM, + .nr_l2streams = IPU7P5_IS_MMU_FW_RD_STREAM_NUM, + .l1_block_sz = { 0x0, 0x8, 0xa }, + .l2_block_sz = { 0x0, 0x2, 0x4 }, + .zlx_nr = IPU7P5_IS_ZLX_UC_RD_NUM, + .zlx_axi_pool = { 0x00000f30 }, + .zlx_en = { 0, 1, 0, 0 }, + .zlx_conf = { 0x0 }, + .uao_p_num = IPU7P5_IS_UAO_UC_RD_PLANENUM, + .uao_p2tlb = { 0x49, 0x4c, 0x4d, 0x0 }, + }, + { + .name = "IS_FW_WR", + .offset = IPU7P5_IS_MMU_FW_WR_OFFSET, + .zlx_offset = IPU7P5_IS_ZLX_UC_WR_OFFSET, + .uao_offset = IPU7P5_IS_UAO_UC_WR_OFFSET, + .info_bits = 0x20005001, + .refill = 0x00002524, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU7P5_IS_MMU_FW_WR_L1_BLOCKNR_REG, + .l2_block = IPU7P5_IS_MMU_FW_WR_L2_BLOCKNR_REG, + .nr_l1streams = IPU7P5_IS_MMU_FW_WR_STREAM_NUM, + .nr_l2streams = IPU7P5_IS_MMU_FW_WR_STREAM_NUM, + .l1_block_sz = { 0x0, 0x8, 0xa }, + .l2_block_sz = { 0x0, 0x2, 0x4 }, + .zlx_nr = IPU7P5_IS_ZLX_UC_WR_NUM, + .zlx_axi_pool = { 0x00000f20 }, + .zlx_en = { 0, 1, 1, 0 }, + .zlx_conf = { 0x0, 0x00010101, 0x00010101, 0x0 }, + .uao_p_num = IPU7P5_IS_UAO_UC_WR_PLANENUM, + .uao_p2tlb = { 0x49, 0x4a, 0x4b, 0x0 }, + }, + { + .name = "IS_DATA_WR_ISOC", + .offset = IPU7P5_IS_MMU_M0_OFFSET, + .zlx_offset = IPU7P5_IS_ZLX_M0_OFFSET, + .uao_offset = IPU7P5_IS_UAO_M0_WR_OFFSET, + .info_bits = 0x20004e01, + .refill = 0x00002120, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU7P5_IS_MMU_M0_L1_BLOCKNR_REG, + .l2_block = IPU7P5_IS_MMU_M0_L2_BLOCKNR_REG, + .nr_l1streams = IPU7P5_IS_MMU_M0_STREAM_NUM, + .nr_l2streams = IPU7P5_IS_MMU_M0_STREAM_NUM, + .l1_block_sz = { 0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10, + 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x01e }, + .l2_block_sz = { 0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10, + 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e }, + .zlx_nr = IPU7P5_IS_ZLX_M0_NUM, + .zlx_axi_pool = { 0x00000f10 }, + .zlx_en = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + .zlx_conf = { 0x00010103, 0x00010103, 0x00010103, 0x00010103, + 0x00010103, 0x00010103, 0x00010103, 0x00010103, + 0x00010103, 0x00010103, 0x00010103, 0x00010103, + 0x00010103, 0x00010103, 0x00010103, 0x00010103 }, + .uao_p_num = IPU7P5_IS_UAO_M0_WR_PLANENUM, + .uao_p2tlb = { 0x00000041, 0x00000042, 0x00000043, 0x00000044, + 0x00000041, 0x00000042, 0x00000043, 0x00000044, + 0x00000041, 0x00000042, 0x00000043, 0x00000044, + 0x00000041, 0x00000042, 0x00000043, 0x00000044 }, + }, + { + .name = "IS_DATA_WR_SNOOP", + .offset = IPU7P5_IS_MMU_M1_OFFSET, + .zlx_offset = IPU7P5_IS_ZLX_M1_OFFSET, + .uao_offset = IPU7P5_IS_UAO_M1_WR_OFFSET, + .info_bits = 0x20004f01, + .refill = 0x00002322, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU7P5_IS_MMU_M1_L1_BLOCKNR_REG, + .l2_block = IPU7P5_IS_MMU_M1_L2_BLOCKNR_REG, + .nr_l1streams = IPU7P5_IS_MMU_M1_STREAM_NUM, + .nr_l2streams = IPU7P5_IS_MMU_M1_STREAM_NUM, + .l1_block_sz = { 0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10, + 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e }, + .l2_block_sz = { 0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10, + 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e }, + .zlx_nr = IPU7P5_IS_ZLX_M1_NUM, + .zlx_axi_pool = { 0x00000f20 }, + .zlx_en = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + .zlx_conf = { 0x00010103, 0x00010103, 0x00010103, 0x00010103, + 0x00010103, 0x00010103, 0x00010103, 0x00010103, + 0x00010103, 0x00010103, 0x00010103, 0x00010103, + 0x00010103, 0x00010103, 0x00010103, 0x00010103 }, + .uao_p_num = IPU7P5_IS_UAO_M1_WR_PLANENUM, + .uao_p2tlb = { 0x00000045, 0x00000046, 0x00000047, 0x00000048, + 0x00000045, 0x00000046, 0x00000047, 0x00000048, + 0x00000045, 0x00000046, 0x00000047, 0x00000048, + 0x00000045, 0x00000046, 0x00000047, 0x00000048 }, + }, +}; + +static struct ipu7_mmu_hw ipu7p5_psys_mmu_hwdata[] = { + { + .name = "PS_FW_RD", + .offset = IPU7P5_PS_MMU_FW_RD_OFFSET, + .zlx_offset = IPU7P5_PS_ZLX_FW_RD_OFFSET, + .uao_offset = IPU7P5_PS_UAO_FW_RD_OFFSET, + .info_bits = 0x20004001, + .refill = 0x00002726, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU7P5_PS_MMU_FW_RD_L1_BLOCKNR_REG, + .l2_block = IPU7P5_PS_MMU_FW_RD_L2_BLOCKNR_REG, + .nr_l1streams = IPU7P5_PS_MMU_FW_RD_STREAM_NUM, + .nr_l2streams = IPU7P5_PS_MMU_FW_RD_STREAM_NUM, + .l1_block_sz = { 0x00, 0x08, 0x0a, 0x0c, 0x0d, 0x0f, 0x11, + 0x12, 0x13, 0x14, 0x16, 0x18, 0x19, 0x1a, + 0x1a, 0x1a }, + .l2_block_sz = { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, + 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, + 0x1c, 0x1e }, + .zlx_nr = IPU7P5_PS_ZLX_FW_RD_NUM, + .zlx_axi_pool = { 0x00000f30 }, + .zlx_en = { 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0 }, + .zlx_conf = { 0x00000000, 0x00010101, 0x00000000, 0x00000000, + 0x00010101, 0x00010101, 0x00000000, 0x00000000, + 0x00000000, 0x00010101, 0x00010101, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + .uao_p_num = IPU7P5_PS_UAO_FW_RD_PLANENUM, + .uao_p2tlb = { 0x2e, 0x35, 0x36, 0x31, 0x37, 0x38, 0x39, 0x32, + 0x33, 0x3a, 0x3b, 0x3c, 0x34, 0x00, 0x00, 0x00 }, + }, + { + .name = "PS_FW_WR", + .offset = IPU7P5_PS_MMU_FW_WR_OFFSET, + .zlx_offset = IPU7P5_PS_ZLX_FW_WR_OFFSET, + .uao_offset = IPU7P5_PS_UAO_FW_WR_OFFSET, + .info_bits = 0x20003e01, + .refill = 0x00002322, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU7P5_PS_MMU_FW_WR_L1_BLOCKNR_REG, + .l2_block = IPU7P5_PS_MMU_FW_WR_L2_BLOCKNR_REG, + .nr_l1streams = IPU7P5_PS_MMU_FW_WR_STREAM_NUM, + .nr_l2streams = IPU7P5_PS_MMU_FW_WR_STREAM_NUM, + .l1_block_sz = { 0x00, 0x08, 0x0a, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x10, 0x10 }, + .l2_block_sz = { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, + 0x10, 0x12 }, + .zlx_nr = IPU7P5_PS_ZLX_FW_WR_NUM, + .zlx_axi_pool = { 0x00000f20 }, + .zlx_en = { 0, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, + .zlx_conf = { 0x00000000, 0x00010101, 0x00010101, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000 }, + .uao_p_num = IPU7P5_PS_UAO_FW_WR_PLANENUM, + .uao_p2tlb = { 0x0000002e, 0x0000002f, 0x00000030, 0x00000031, + 0x00000032, 0x00000033, 0x00000034, 0x00000000, + 0x00000000, 0x00000000 }, + }, + { + .name = "PS_DATA_RD", + .offset = IPU7P5_PS_MMU_SRT_RD_OFFSET, + .zlx_offset = IPU7P5_PS_ZLX_DATA_RD_OFFSET, + .uao_offset = IPU7P5_PS_UAO_SRT_RD_OFFSET, + .info_bits = 0x20003f01, + .refill = 0x00002524, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU7P5_PS_MMU_SRT_RD_L1_BLOCKNR_REG, + .l2_block = IPU7P5_PS_MMU_SRT_RD_L2_BLOCKNR_REG, + .nr_l1streams = IPU7P5_PS_MMU_SRT_RD_STREAM_NUM, + .nr_l2streams = IPU7P5_PS_MMU_SRT_RD_STREAM_NUM, + .l1_block_sz = { 0x00, 0x04, 0x06, 0x08, 0x0b, 0x0d, 0x0f, 0x13, + 0x17, 0x19, 0x1b, 0x1d, 0x1f, 0x2b, 0x33, 0x3f, + 0x47, 0x49, 0x4b, 0x4c, 0x4d, 0x4e }, + .l2_block_sz = { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, + 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, + 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a }, + .zlx_nr = IPU7P5_PS_ZLX_DATA_RD_NUM, + .zlx_axi_pool = { 0x00000f30 }, + .zlx_en = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 0, 0 }, + .zlx_conf = { 0x00030303, 0x00010101, 0x00010101, 0x00030202, + 0x00010101, 0x00010101, 0x00030303, 0x00030303, + 0x00010101, 0x00030800, 0x00030500, 0x00020101, + 0x00042000, 0x00031000, 0x00042000, 0x00031000, + 0x00020400, 0x00010101, 0x00000000, 0x00000000, + 0x00000000, 0x00000000 }, + .uao_p_num = IPU7P5_PS_UAO_SRT_RD_PLANENUM, + .uao_p2tlb = { 0x0000001c, 0x0000001d, 0x0000001e, 0x0000001f, + 0x00000020, 0x00000021, 0x00000022, 0x00000023, + 0x00000024, 0x00000025, 0x00000026, 0x00000027, + 0x00000028, 0x00000029, 0x0000002a, 0x0000002b, + 0x0000002c, 0x0000002d, 0x00000000, 0x00000000, + 0x00000000, 0x00000000 }, + }, + { + .name = "PS_DATA_WR", + .offset = IPU7P5_PS_MMU_SRT_WR_OFFSET, + .zlx_offset = IPU7P5_PS_ZLX_DATA_WR_OFFSET, + .uao_offset = IPU7P5_PS_UAO_SRT_WR_OFFSET, + .info_bits = 0x20003d01, + .refill = 0x00002120, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU7P5_PS_MMU_SRT_WR_L1_BLOCKNR_REG, + .l2_block = IPU7P5_PS_MMU_SRT_WR_L2_BLOCKNR_REG, + .nr_l1streams = IPU7P5_PS_MMU_SRT_WR_STREAM_NUM, + .nr_l2streams = IPU7P5_PS_MMU_SRT_WR_STREAM_NUM, + .l1_block_sz = { 0x00, 0x02, 0x06, 0x0a, 0x0c, 0x0e, 0x10, 0x12, + 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, 0x20, 0x22, + 0x24, 0x28, 0x2a, 0x36, 0x3e, 0x40, 0x42, 0x4e, + 0x56, 0x5c, 0x68, 0x70, 0x76, 0x77, 0x78, 0x79 }, + .l2_block_sz = { 0x00, 0x02, 0x06, 0x0a, 0x0c, 0x0e, 0x10, 0x12, + 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, 0x20, 0x22, + 0x24, 0x28, 0x2a, 0x36, 0x3e, 0x40, 0x42, 0x4e, + 0x56, 0x5c, 0x68, 0x70, 0x76, 0x77, 0x78, 0x79 }, + .zlx_nr = IPU7P5_PS_ZLX_DATA_WR_NUM, + .zlx_axi_pool = { 0x00000f50 }, + .zlx_en = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, + .zlx_conf = { 0x00010102, 0x00030103, 0x00030103, 0x00010101, + 0x00010101, 0x00030101, 0x00010101, 0x38010101, + 0x00000000, 0x00000000, 0x38010101, 0x38010101, + 0x38010101, 0x38010101, 0x38010101, 0x38010101, + 0x00030303, 0x00010101, 0x00042000, 0x00031000, + 0x00010101, 0x00010101, 0x00042000, 0x00031000, + 0x00031000, 0x00042000, 0x00031000, 0x00031000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + .uao_p_num = IPU7P5_PS_UAO_SRT_WR_PLANENUM, + .uao_p2tlb = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x00, 0x00, 0x00, 0x00 }, + }, +}; + +static const struct ipu7_mmu_hwdata ipu7_mmu_hwdata_lookup[IPU_SUBSYS_NUM] = { + [IPU_PSYS] = { + .hwdata = ipu7_psys_mmu_hwdata, + .nr_mmus = ARRAY_SIZE(ipu7_psys_mmu_hwdata), + }, + [IPU_ISYS] = { + .hwdata = ipu7_isys_mmu_hwdata, + .nr_mmus = ARRAY_SIZE(ipu7_isys_mmu_hwdata), + }, +}; + +static const struct ipu7_mmu_hwdata ipu7p5_mmu_hwdata_lookup[IPU_SUBSYS_NUM] = { + [IPU_PSYS] = { + .hwdata = ipu7p5_psys_mmu_hwdata, + .nr_mmus = ARRAY_SIZE(ipu7p5_psys_mmu_hwdata), + }, + [IPU_ISYS] = { + .hwdata = ipu7p5_isys_mmu_hwdata, + .nr_mmus = ARRAY_SIZE(ipu7p5_isys_mmu_hwdata), + }, +}; + +static void __ipu7_tlb_invalidate(struct ipu6_mmu *mmu) +{ + struct ipu7_mmu_hw *mmu_hw = mmu->ipu7_mmu_hw; + unsigned long flags; + unsigned int i; + int ret; + u32 val; + + spin_lock_irqsave(&mmu->ready_lock, flags); + if (!mmu->ready) { + spin_unlock_irqrestore(&mmu->ready_lock, flags); + return; + } + + for (i = 0; i < mmu->nr_mmus; i++) { + writel(0xffffffffU, mmu_hw[i].base + + IPU7_MMU_REG_INVALIDATE_0); + + /* Need check with HW, use l1streams or l2streams */ + if (mmu_hw[i].nr_l2streams > 32) + writel(0xffffffffU, mmu_hw[i].base + + IPU7_MMU_REG_INVALIDATE_1); + + /* + * The TLB invalidation is a "single cycle" (IOMMU clock cycles) + * When the actual MMIO write reaches the IPU TLB Invalidate + * register, wmb() will force the TLB invalidate out if the CPU + * attempts to update the IOMMU page table (or sooner). + */ + wmb(); + + /* wait invalidation done */ + ret = readl_poll_timeout_atomic(mmu_hw[i].base + + IPU7_MMU_REG_INVALIDATION_STATUS, + val, !(val & 0x1U), 500, + IPU7_MMU_TLB_INVALIDATE_TIMEOUT); + if (ret) + dev_err(mmu->dev, "MMU[%u] TLB invalidate failed\n", i); + } + + spin_unlock_irqrestore(&mmu->ready_lock, flags); +} + +static int __ipu7_mmu_hw_init(struct ipu6_mmu *mmu) +{ + struct ipu6_mmu_info *mmu_info; + struct ipu7_mmu_hw *mmu_hw = mmu->ipu7_mmu_hw; + unsigned int i, j; + + mmu_info = mmu->dmap->mmu_info; + for (i = 0; i < mmu->nr_mmus; i++) { + /* Write page table address per MMU */ + writel((phys_addr_t)mmu_info->l1_pt_dma, + mmu_hw[i].base + IPU7_MMU_REG_PAGE_TABLE_BASE_ADDR); + + /* Set info bits and axi_refill per MMU */ + writel(mmu_hw[i].info_bits, + mmu_hw[i].base + IPU7_MMU_REG_USER_INFO_BITS); + writel(mmu_hw[i].refill, mmu_hw[i].base + IPU7_MMU_REG_AXI_REFILL_IF_ID); + writel(mmu_hw[i].collapse_en_bitmap, + mmu_hw[i].base + IPU7_MMU_REG_COLLAPSE_ENABLE_BITMAP); + + if (mmu_hw[i].at_sp_arb_cfg) + writel(mmu_hw[i].at_sp_arb_cfg, + mmu_hw[i].base + IPU7_MMU_REG_AT_SP_ARB_CFG); + + /* Default irq configuration */ + writel(0x3ff, mmu_hw[i].base + IPU7_MMU_REG_IRQ_MASK); + writel(0x3ff, mmu_hw[i].base + IPU7_MMU_REG_IRQ_ENABLE); + + /* Configure MMU TLB stream configuration for L1/L2 */ + for (j = 0; j < mmu_hw[i].nr_l1streams; j++) { + writel(mmu_hw[i].l1_block_sz[j], mmu_hw[i].base + + mmu_hw[i].l1_block + 4U * j); + } + + for (j = 0; j < mmu_hw[i].nr_l2streams; j++) { + writel(mmu_hw[i].l2_block_sz[j], mmu_hw[i].base + + mmu_hw[i].l2_block + 4U * j); + } + + for (j = 0; j < mmu_hw[i].uao_p_num; j++) { + if (!mmu_hw[i].uao_p2tlb[j]) + continue; + writel(mmu_hw[i].uao_p2tlb[j], mmu_hw[i].uao_base + 4U * j); + } + } + + for (i = 0; i < mmu->nr_mmus; i++) { + for (j = 0; j < IPU7_ZLX_POOL_NUM; j++) { + if (!mmu_hw[i].zlx_axi_pool[j]) + continue; + writel(mmu_hw[i].zlx_axi_pool[j], + mmu_hw[i].zlx_base + IPU7_ZLX_REG_AXI_POOL + j * 0x4U); + } + + for (j = 0; j < mmu_hw[i].zlx_nr; j++) { + if (!mmu_hw[i].zlx_conf[j]) + continue; + + writel(mmu_hw[i].zlx_conf[j], + mmu_hw[i].zlx_base + IPU7_ZLX_REG_CONF + j * 0x8U); + } + + for (j = 0; j < mmu_hw[i].zlx_nr; j++) { + if (!mmu_hw[i].zlx_en[j]) + continue; + + writel(mmu_hw[i].zlx_en[j], + mmu_hw[i].zlx_base + IPU7_ZLX_REG_EN + j * 0x8U); + } + } + + return 0; +} + +static int __ipu7_mmu_init_hw_data(struct ipu6_mmu *mmu, struct device *dev, + void __iomem *base) +{ + struct ipu6_device *isp = pci_get_drvdata(to_pci_dev(dev)); + const struct ipu7_mmu_hwdata *lookup; + struct ipu7_mmu_hw *mmu_hw, *src; + unsigned int i, nr_mmus; + + if (mmu->mmid >= IPU_SUBSYS_NUM) + return -EINVAL; + + lookup = IS_IPU7P5(isp) ? ipu7p5_mmu_hwdata_lookup : + ipu7_mmu_hwdata_lookup; + + src = lookup[mmu->mmid].hwdata; + nr_mmus = lookup[mmu->mmid].nr_mmus; + + mmu_hw = devm_kcalloc(dev, nr_mmus, sizeof(*mmu_hw), GFP_KERNEL); + if (!mmu_hw) + return -ENOMEM; + + for (i = 0; i < nr_mmus; i++) { + if (src[i].nr_l1streams > IPU7_MMU_MAX_TLB_L1_STREAMS || + src[i].nr_l2streams > IPU7_MMU_MAX_TLB_L2_STREAMS) + return -EINVAL; + + mmu_hw[i] = src[i]; + mmu_hw[i].base = base + src[i].offset; + mmu_hw[i].zlx_base = base + src[i].zlx_offset; + mmu_hw[i].uao_base = base + src[i].uao_offset; + } + + mmu->nr_mmus = nr_mmus; + mmu->ipu7_mmu_hw = mmu_hw; + + return 0; +} + +const struct ipu6_mmu_hw_ops ipu7_mmu_ops = { + .init_hw_data = __ipu7_mmu_init_hw_data, + .hw_init = __ipu7_mmu_hw_init, + .tlb_invalidate = __ipu7_tlb_invalidate, +}; diff --git a/drivers/media/pci/intel/ipu6/ipu7-mmu-hw.h b/drivers/media/pci/intel/ipu6/ipu7-mmu-hw.h new file mode 100644 index 000000000000..ba31ed31b245 --- /dev/null +++ b/drivers/media/pci/intel/ipu6/ipu7-mmu-hw.h @@ -0,0 +1,254 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2026 Intel Corporation */ + +#ifndef IPU7_MMU_HW_H +#define IPU7_MMU_HW_H + +#define IPU7_MMU_REG_IRQ_MASK 0x2c +#define IPU7_MMU_REG_IRQ_ENABLE 0x30 +#define IPU7_MMU_REG_PAGE_TABLE_BASE_ADDR 0x08 +#define IPU7_MMU_REG_USER_INFO_BITS 0x0c +#define IPU7_MMU_REG_AXI_REFILL_IF_ID 0x10 +#define IPU7_MMU_REG_COLLAPSE_ENABLE_BITMAP 0x18 +#define IPU7_MMU_REG_AT_SP_ARB_CFG 0x20 + +#define IPU7_ZLX_REG_AXI_POOL 0x0 +#define IPU7_ZLX_REG_EN 0x20 +#define IPU7_ZLX_REG_CONF 0x24 +#define IPU7_ZLX_POOL_NUM 8U + +#define IPU7_MMU_MAX_TLB_L1_STREAMS 40U +#define IPU7_MMU_MAX_TLB_L2_STREAMS 40U +#define IPU7_UAO_PLANE_MAX_NUM 64U +#define IPU7_ZLX_MAX_NUM 32U + +#define IPU7_MMU_REG_INVALIDATE_0 0x00 +#define IPU7_MMU_REG_INVALIDATE_1 0x04 +#define IPU7_MMU_REG_INVALIDATION_STATUS 0x24 +#define IPU7_MMU_TLB_INVALIDATE_TIMEOUT 2000 + +#define IPU7_FW_CODE_REGION_SIZE 0x1000000 /* 16MB */ +#define IPU7_FW_CODE_REGION_START 0x4000000 /* 64MB */ +#define IPU7_FW_CODE_REGION_END (IPU7_FW_CODE_REGION_START + \ + IPU7_FW_CODE_REGION_SIZE) /* 80MB */ + +#define IPU7_IS_MMU_FW_RD_OFFSET 0x274000 +#define IPU7_IS_MMU_FW_RD_STREAM_NUM 3 +#define IPU7_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 +#define IPU7_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60 + +#define IPU7_IS_MMU_FW_WR_OFFSET 0x275000 +#define IPU7_IS_MMU_FW_WR_STREAM_NUM 3 +#define IPU7_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 +#define IPU7_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60 + +#define IPU7_IS_MMU_M0_OFFSET 0x276000 +#define IPU7_IS_MMU_M0_STREAM_NUM 8 +#define IPU7_IS_MMU_M0_L1_BLOCKNR_REG 0x54 +#define IPU7_IS_MMU_M0_L2_BLOCKNR_REG 0x74 + +#define IPU7_IS_MMU_M1_OFFSET 0x277000 +#define IPU7_IS_MMU_M1_STREAM_NUM 16 +#define IPU7_IS_MMU_M1_L1_BLOCKNR_REG 0x54 +#define IPU7_IS_MMU_M1_L2_BLOCKNR_REG 0x94 + +#define IPU7_PS_MMU_FW_RD_OFFSET 0x148000 +#define IPU7_PS_MMU_FW_RD_STREAM_NUM 20 +#define IPU7_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 +#define IPU7_PS_MMU_FW_RD_L2_BLOCKNR_REG 0xa4 + +#define IPU7_PS_MMU_FW_WR_OFFSET 0x149000 +#define IPU7_PS_MMU_FW_WR_STREAM_NUM 10 +#define IPU7_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 +#define IPU7_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x7c + +#define IPU7_PS_MMU_SRT_RD_OFFSET 0x14a000 +#define IPU7_PS_MMU_SRT_RD_STREAM_NUM 40 +#define IPU7_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54 +#define IPU7_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xf4 + +#define IPU7_PS_MMU_SRT_WR_OFFSET 0x14b000 +#define IPU7_PS_MMU_SRT_WR_STREAM_NUM 40 +#define IPU7_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54 +#define IPU7_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xf4 + +#define IPU7_IS_UAO_UC_RD_OFFSET 0x27c000 +#define IPU7_IS_UAO_UC_RD_PLANENUM 4 + +#define IPU7_IS_UAO_UC_WR_OFFSET 0x27d000 +#define IPU7_IS_UAO_UC_WR_PLANENUM 4 + +#define IPU7_IS_UAO_M0_WR_OFFSET 0x27e000 +#define IPU7_IS_UAO_M0_WR_PLANENUM 8 + +#define IPU7_IS_UAO_M1_WR_OFFSET 0x27f000 +#define IPU7_IS_UAO_M1_WR_PLANENUM 16 + +#define IPU7_PS_UAO_FW_RD_OFFSET 0x156000 +#define IPU7_PS_UAO_FW_RD_PLANENUM 20 + +#define IPU7_PS_UAO_FW_WR_OFFSET 0x157000 +#define IPU7_PS_UAO_FW_WR_PLANENUM 16 + +#define IPU7_PS_UAO_SRT_RD_OFFSET 0x154000 +#define IPU7_PS_UAO_SRT_RD_PLANENUM 40 + +#define IPU7_PS_UAO_SRT_WR_OFFSET 0x155000 +#define IPU7_PS_UAO_SRT_WR_PLANENUM 40 + +#define IPU7_IS_ZLX_UC_RD_OFFSET 0x278000 +#define IPU7_IS_ZLX_UC_WR_OFFSET 0x279000 +#define IPU7_IS_ZLX_M0_OFFSET 0x27a000 +#define IPU7_IS_ZLX_M1_OFFSET 0x27b000 +#define IPU7_IS_ZLX_UC_RD_NUM 4 +#define IPU7_IS_ZLX_UC_WR_NUM 4 +#define IPU7_IS_ZLX_M0_NUM 8 +#define IPU7_IS_ZLX_M1_NUM 16 + +#define IPU7_PS_ZLX_DATA_RD_OFFSET 0x14e000 +#define IPU7_PS_ZLX_DATA_WR_OFFSET 0x14f000 +#define IPU7_PS_ZLX_FW_RD_OFFSET 0x150000 +#define IPU7_PS_ZLX_FW_WR_OFFSET 0x151000 +#define IPU7_PS_ZLX_DATA_RD_NUM 32 +#define IPU7_PS_ZLX_DATA_WR_NUM 32 +#define IPU7_PS_ZLX_FW_RD_NUM 16 +#define IPU7_PS_ZLX_FW_WR_NUM 10 + +/* IPU7P5 */ +/* IS MMU Cmd RD */ +#define IPU7P5_IS_MMU_FW_RD_OFFSET 0x274000 +#define IPU7P5_IS_MMU_FW_RD_STREAM_NUM 3 +#define IPU7P5_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 +#define IPU7P5_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60 + +/* IS MMU Cmd WR */ +#define IPU7P5_IS_MMU_FW_WR_OFFSET 0x275000 +#define IPU7P5_IS_MMU_FW_WR_STREAM_NUM 3 +#define IPU7P5_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 +#define IPU7P5_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60 + +/* IS MMU Data WR Snoop */ +#define IPU7P5_IS_MMU_M0_OFFSET 0x276000 +#define IPU7P5_IS_MMU_M0_STREAM_NUM 16 +#define IPU7P5_IS_MMU_M0_L1_BLOCKNR_REG 0x54 +#define IPU7P5_IS_MMU_M0_L2_BLOCKNR_REG 0x94 + +/* IS MMU Data WR ISOC */ +#define IPU7P5_IS_MMU_M1_OFFSET 0x277000 +#define IPU7P5_IS_MMU_M1_STREAM_NUM 16 +#define IPU7P5_IS_MMU_M1_L1_BLOCKNR_REG 0x54 +#define IPU7P5_IS_MMU_M1_L2_BLOCKNR_REG 0x94 + +/* PS MMU FW RD */ +#define IPU7P5_PS_MMU_FW_RD_OFFSET 0x148000 +#define IPU7P5_PS_MMU_FW_RD_STREAM_NUM 16 +#define IPU7P5_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 +#define IPU7P5_PS_MMU_FW_RD_L2_BLOCKNR_REG 0x94 + +/* PS MMU FW WR */ +#define IPU7P5_PS_MMU_FW_WR_OFFSET 0x149000 +#define IPU7P5_PS_MMU_FW_WR_STREAM_NUM 10 +#define IPU7P5_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 +#define IPU7P5_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x7c + +/* PS MMU FW Data RD VC0 */ +#define IPU7P5_PS_MMU_SRT_RD_OFFSET 0x14a000 +#define IPU7P5_PS_MMU_SRT_RD_STREAM_NUM 22 +#define IPU7P5_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54 +#define IPU7P5_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xac + +/* PS MMU FW Data WR VC0 */ +#define IPU7P5_PS_MMU_SRT_WR_OFFSET 0x14b000 +#define IPU7P5_PS_MMU_SRT_WR_STREAM_NUM 32 +#define IPU7P5_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54 +#define IPU7P5_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xd4 + +/* IS UAO UC RD */ +#define IPU7P5_IS_UAO_UC_RD_OFFSET 0x27c000 +#define IPU7P5_IS_UAO_UC_RD_PLANENUM 4 + +/* IS UAO UC WR */ +#define IPU7P5_IS_UAO_UC_WR_OFFSET 0x27d000 +#define IPU7P5_IS_UAO_UC_WR_PLANENUM 4 + +/* IS UAO M0 WR */ +#define IPU7P5_IS_UAO_M0_WR_OFFSET 0x27e000 +#define IPU7P5_IS_UAO_M0_WR_PLANENUM 16 + +/* IS UAO M1 WR */ +#define IPU7P5_IS_UAO_M1_WR_OFFSET 0x27f000 +#define IPU7P5_IS_UAO_M1_WR_PLANENUM 16 + +/* PS UAO FW RD */ +#define IPU7P5_PS_UAO_FW_RD_OFFSET 0x156000 +#define IPU7P5_PS_UAO_FW_RD_PLANENUM 16 + +/* PS UAO FW WR */ +#define IPU7P5_PS_UAO_FW_WR_OFFSET 0x157000 +#define IPU7P5_PS_UAO_FW_WR_PLANENUM 10 + +/* PS UAO SRT RD */ +#define IPU7P5_PS_UAO_SRT_RD_OFFSET 0x154000 +#define IPU7P5_PS_UAO_SRT_RD_PLANENUM 22 + +/* PS UAO SRT WR */ +#define IPU7P5_PS_UAO_SRT_WR_OFFSET 0x155000 +#define IPU7P5_PS_UAO_SRT_WR_PLANENUM 32 + +#define IPU7P5_IS_ZLX_UC_RD_OFFSET 0x278000 +#define IPU7P5_IS_ZLX_UC_WR_OFFSET 0x279000 +#define IPU7P5_IS_ZLX_M0_OFFSET 0x27a000 +#define IPU7P5_IS_ZLX_M1_OFFSET 0x27b000 +#define IPU7P5_IS_ZLX_UC_RD_NUM 4 +#define IPU7P5_IS_ZLX_UC_WR_NUM 4 +#define IPU7P5_IS_ZLX_M0_NUM 16 +#define IPU7P5_IS_ZLX_M1_NUM 16 + +#define IPU7P5_PS_ZLX_DATA_RD_OFFSET 0x14e000 +#define IPU7P5_PS_ZLX_DATA_WR_OFFSET 0x14f000 +#define IPU7P5_PS_ZLX_FW_RD_OFFSET 0x150000 +#define IPU7P5_PS_ZLX_FW_WR_OFFSET 0x151000 +#define IPU7P5_PS_ZLX_DATA_RD_NUM 22 +#define IPU7P5_PS_ZLX_DATA_WR_NUM 32 +#define IPU7P5_PS_ZLX_FW_RD_NUM 16 +#define IPU7P5_PS_ZLX_FW_WR_NUM 10 + +struct ipu7_mmu_hw { + char name[32]; + + void __iomem *base; + void __iomem *zlx_base; + void __iomem *uao_base; + + u32 offset; + u32 zlx_offset; + u32 uao_offset; + + u32 info_bits; + u32 refill; + u32 collapse_en_bitmap; + u32 at_sp_arb_cfg; + + u32 l1_block; + u32 l2_block; + + u8 nr_l1streams; + u8 nr_l2streams; + u32 l1_block_sz[IPU7_MMU_MAX_TLB_L1_STREAMS]; + u32 l2_block_sz[IPU7_MMU_MAX_TLB_L2_STREAMS]; + + u8 zlx_nr; + u32 zlx_axi_pool[IPU7_ZLX_POOL_NUM]; + u32 zlx_en[IPU7_ZLX_MAX_NUM]; + u32 zlx_conf[IPU7_ZLX_MAX_NUM]; + + u32 uao_p_num; + u32 uao_p2tlb[IPU7_UAO_PLANE_MAX_NUM]; +}; + +struct ipu7_mmu_hwdata { + struct ipu7_mmu_hw *hwdata; + unsigned int nr_mmus; +}; + +#endif diff --git a/drivers/media/pci/intel/ipu6/ipu7-platform-regs.h b/drivers/media/pci/intel/ipu6/ipu7-platform-regs.h new file mode 100644 index 000000000000..39cd5c0890e5 --- /dev/null +++ b/drivers/media/pci/intel/ipu6/ipu7-platform-regs.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2026 Intel Corporation */ + +#ifndef IPU7_PLATFORM_REGS_H +#define IPU7_PLATFORM_REGS_H + +#define IPU7_IS_UC_CTRL_BASE 0x230000 +#define IPU7_ISYS_DMEM_OFFSET 0x200000 +#define IPU7_PS_UC_CTRL_BASE 0x130000 +#define IPU7_PSYS_DMEM_OFFSET 0x100000 + +#define IPU7_IS_IO_BASE 0x280000 +#define IPU7_IS_IO_CSI2_GPREGS_BASE (IPU7_IS_IO_BASE + 0x53400) + +#define IPU7_IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE (IPU7_IS_IO_BASE + 0x49000) +#define IPU7_IRQ_CTL_EDGE 0x0 +#define IPU7_IRQ_CTL_MASK 0x4 +#define IPU7_IRQ_CTL_STATUS 0x8 +#define IPU7_IRQ_CTL_CLEAR 0xc +#define IPU7_IRQ_CTL_ENABLE 0x10 +#define IPU7_CSI_RX_LEGACY_IRQ_MASK 0x1ff + +#define IPU7_TO_SW_IRQ_CNTL_EDGE 0x4000 +#define IPU7_TO_SW_IRQ_CNTL_MASK_N 0x4004 +#define IPU7_TO_SW_IRQ_CNTL_STATUS 0x4008 +#define IPU7_TO_SW_IRQ_CNTL_CLEAR 0x400c +#define IPU7_TO_SW_IRQ_CNTL_ENABLE 0x4010 +#define IPU7_IS_UC_TO_SW_IRQ_MASK 0xf +#define IPU7_TO_SW_IRQ_FW BIT(0) +#define IPU7_REG_PRINTF_AXI_CNTL 0x301c + +#endif diff --git a/drivers/media/pci/intel/ivsc/mei_ace.c b/drivers/media/pci/intel/ivsc/mei_ace.c index b306a320b70f..bb57656fc85a 100644 --- a/drivers/media/pci/intel/ivsc/mei_ace.c +++ b/drivers/media/pci/intel/ivsc/mei_ace.c @@ -414,13 +414,13 @@ static int mei_ace_setup_dev_link(struct mei_ace *ace) /* setup link between mei_ace and mei_csi */ ace->csi_link = device_link_add(csi_dev, dev, DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE | DL_FLAG_STATELESS); - put_device(csi_dev); if (!ace->csi_link) { ret = -EINVAL; dev_err(dev, "failed to link to %s\n", dev_name(csi_dev)); - goto err; + goto err_put; } + put_device(csi_dev); ace->csi_dev = csi_dev; return 0; diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c b/drivers/media/pci/intel/ivsc/mei_csi.c index c2917e156345..005b1414a252 100644 --- a/drivers/media/pci/intel/ivsc/mei_csi.c +++ b/drivers/media/pci/intel/ivsc/mei_csi.c @@ -27,7 +27,6 @@ #include <linux/workqueue.h> #include <media/ipu-bridge.h> -#include <media/ipu6-pci-table.h> #include <media/v4l2-async.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-fwnode.h> @@ -338,6 +337,7 @@ static int mei_csi_init_state(struct v4l2_subdev *sd, } static int mei_csi_set_fmt(struct v4l2_subdev *sd, + const struct v4l2_subdev_client_info *ci, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { @@ -644,13 +644,9 @@ static int mei_csi_probe(struct mei_cl_device *cldev, struct device *dev = &cldev->dev; struct pci_dev *ipu; struct mei_csi *csi; - unsigned int i; int ret; - for (i = 0, ipu = NULL; !ipu && ipu6_pci_tbl[i].vendor; i++) - ipu = pci_get_device(ipu6_pci_tbl[i].vendor, - ipu6_pci_tbl[i].device, NULL); - + ipu = ipu_bridge_get_ipu6(); if (!ipu) return -ENODEV; diff --git a/drivers/media/pci/ivtv/ivtv-controls.c b/drivers/media/pci/ivtv/ivtv-controls.c index f087a12c4ebd..816e634de1eb 100644 --- a/drivers/media/pci/ivtv/ivtv-controls.c +++ b/drivers/media/pci/ivtv/ivtv-controls.c @@ -60,7 +60,7 @@ static int ivtv_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val) format.format.width = cxhdl->width / (is_mpeg1 ? 2 : 1); format.format.height = cxhdl->height; format.format.code = MEDIA_BUS_FMT_FIXED; - v4l2_subdev_call(itv->sd_video, pad, set_fmt, NULL, &format); + v4l2_subdev_call(itv->sd_video, pad, set_fmt, NULL, NULL, &format); return 0; } diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c index fc95f0bf48d5..aba55fcc1e22 100644 --- a/drivers/media/pci/ivtv/ivtv-ioctl.c +++ b/drivers/media/pci/ivtv/ivtv-ioctl.c @@ -587,7 +587,7 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f format.format.width = fmt->fmt.pix.width; format.format.height = h; format.format.code = MEDIA_BUS_FMT_FIXED; - v4l2_subdev_call(itv->sd_video, pad, set_fmt, NULL, &format); + v4l2_subdev_call(itv->sd_video, pad, set_fmt, NULL, NULL, &format); return ivtv_g_fmt_vid_cap(file, fh, fmt); } diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index 2f5b258d682b..507ad23776e0 100644 --- a/drivers/media/pci/saa7134/saa7134-core.c +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -1368,7 +1368,7 @@ static int __maybe_unused saa7134_buffer_requeue(struct saa7134_dev *dev, return 0; } -static int __maybe_unused saa7134_suspend(struct device *dev_d) +static int saa7134_suspend(struct device *dev_d) { struct pci_dev *pci_dev = to_pci_dev(dev_d); struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); @@ -1400,7 +1400,7 @@ static int __maybe_unused saa7134_suspend(struct device *dev_d) return 0; } -static int __maybe_unused saa7134_resume(struct device *dev_d) +static int saa7134_resume(struct device *dev_d) { struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d); struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev); @@ -1484,14 +1484,14 @@ EXPORT_SYMBOL(saa7134_ts_unregister); /* ----------------------------------------------------------- */ -static SIMPLE_DEV_PM_OPS(saa7134_pm_ops, saa7134_suspend, saa7134_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(saa7134_pm_ops, saa7134_suspend, saa7134_resume); static struct pci_driver saa7134_pci_driver = { .name = "saa7134", .id_table = saa7134_pci_tbl, .probe = saa7134_initdev, .remove = saa7134_finidev, - .driver.pm = &saa7134_pm_ops, + .driver.pm = pm_sleep_ptr(&saa7134_pm_ops), }; static int __init saa7134_init(void) diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index 8c4f70e4177d..d04a68bb05d8 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -122,7 +122,7 @@ static int empress_s_fmt_vid_cap(struct file *file, void *priv, }; v4l2_fill_mbus_format(&format.format, &f->fmt.pix, MEDIA_BUS_FMT_FIXED); - saa_call_all(dev, pad, set_fmt, NULL, &format); + saa_call_all(dev, pad, set_fmt, NULL, NULL, &format); v4l2_fill_pix_format(&f->fmt.pix, &format.format); f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; @@ -145,7 +145,7 @@ static int empress_try_fmt_vid_cap(struct file *file, void *priv, }; v4l2_fill_mbus_format(&format.format, &f->fmt.pix, MEDIA_BUS_FMT_FIXED); - saa_call_all(dev, pad, set_fmt, &pad_state, &format); + saa_call_all(dev, pad, set_fmt, NULL, &pad_state, &format); v4l2_fill_pix_format(&f->fmt.pix, &format.format); f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c index 7f6680de3156..b2a2372d149a 100644 --- a/drivers/media/pci/saa7134/saa7134-input.c +++ b/drivers/media/pci/saa7134/saa7134-input.c @@ -769,7 +769,8 @@ int saa7134_input_init1(struct saa7134_dev *dev) } ir = kzalloc_obj(*ir); - rc = rc_allocate_device(RC_DRIVER_SCANCODE); + rc = rc_allocate_device(raw_decode ? + RC_DRIVER_IR_RAW : RC_DRIVER_SCANCODE); if (!ir || !rc) { err = -ENOMEM; goto err_out_free; @@ -792,10 +793,8 @@ int saa7134_input_init1(struct saa7134_dev *dev) rc->priv = dev; rc->open = saa7134_ir_open; rc->close = saa7134_ir_close; - if (raw_decode) { - rc->driver_type = RC_DRIVER_IR_RAW; + if (raw_decode) rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; - } rc->device_name = saa7134_boards[dev->board].name; rc->input_phys = ir->phys; diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c index d759e8a87e24..129d41c1862b 100644 --- a/drivers/media/pci/saa7146/mxb.c +++ b/drivers/media/pci/saa7146/mxb.c @@ -37,7 +37,7 @@ /* global variable */ static int mxb_num; -/* initial frequence the tuner will be tuned to. +/* initial frequency the tuner will be tuned to. in verden (lower saxony, germany) 4148 is a channel called "phoenix" */ static int freq = 4148; @@ -429,7 +429,7 @@ err: /* some stuff is done via direct write to the registers */ /* this is ugly, but because of the fact that this is completely - hardware dependend, it should be done directly... */ + hardware dependent, it should be done directly... */ saa7146_write(dev, DD1_STREAM_B, 0x00000000); saa7146_write(dev, DD1_INIT, 0x02000200); saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c index ac5eb6b923e2..debcfaaf36dc 100644 --- a/drivers/media/pci/saa7164/saa7164-core.c +++ b/drivers/media/pci/saa7164/saa7164-core.c @@ -742,35 +742,6 @@ u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev) return reg; } -/* TODO: Debugging func, remove */ -void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr) -{ - int i; - - dprintk(1, "--------------------> 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); - - for (i = 0; i < 0x100; i += 16) - dprintk(1, "region0[0x%08x] = %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", - i, - (u8)saa7164_readb(addr + i + 0), - (u8)saa7164_readb(addr + i + 1), - (u8)saa7164_readb(addr + i + 2), - (u8)saa7164_readb(addr + i + 3), - (u8)saa7164_readb(addr + i + 4), - (u8)saa7164_readb(addr + i + 5), - (u8)saa7164_readb(addr + i + 6), - (u8)saa7164_readb(addr + i + 7), - (u8)saa7164_readb(addr + i + 8), - (u8)saa7164_readb(addr + i + 9), - (u8)saa7164_readb(addr + i + 10), - (u8)saa7164_readb(addr + i + 11), - (u8)saa7164_readb(addr + i + 12), - (u8)saa7164_readb(addr + i + 13), - (u8)saa7164_readb(addr + i + 14), - (u8)saa7164_readb(addr + i + 15) - ); -} - static void saa7164_dump_hwdesc(struct saa7164_dev *dev) { dprintk(1, "@0x%p hwdesc sizeof(struct tmComResHWDescr) = %d bytes\n", @@ -856,7 +827,7 @@ static void saa7164_get_descriptors(struct saa7164_dev *dev) saa7164_dump_hwdesc(dev); if (dev->intfdesc.bLength != sizeof(struct tmComResInterfaceDescr)) { - printk(KERN_ERR "struct struct tmComResInterfaceDescr is mangled\n"); + printk(KERN_ERR "struct tmComResInterfaceDescr is mangled\n"); printk(KERN_ERR "Need %x got %d\n", dev->intfdesc.bLength, (u32)sizeof(struct tmComResInterfaceDescr)); } else @@ -1344,7 +1315,6 @@ static int saa7164_initdev(struct pci_dev *pci_dev, } saa7164_get_descriptors(dev); - saa7164_dumpregs(dev, 0); saa7164_getcurrentfirmwareversion(dev); saa7164_getfirmwarestatus(dev); err = saa7164_bus_setup(dev); diff --git a/drivers/media/pci/saa7164/saa7164.h b/drivers/media/pci/saa7164/saa7164.h index 94e987e7b5e5..53ef9c3343ba 100644 --- a/drivers/media/pci/saa7164/saa7164.h +++ b/drivers/media/pci/saa7164/saa7164.h @@ -490,7 +490,6 @@ extern unsigned int vbi_buffers; /* ----------------------------------------------------------- */ /* saa7164-core.c */ -void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr); void saa7164_getfirmwarestatus(struct saa7164_dev *dev); u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev); void saa7164_histogram_update(struct saa7164_histogram *hg, u32 val); diff --git a/drivers/media/pci/tw68/tw68-core.c b/drivers/media/pci/tw68/tw68-core.c index 509d7ddec150..0909b2d9fcdd 100644 --- a/drivers/media/pci/tw68/tw68-core.c +++ b/drivers/media/pci/tw68/tw68-core.c @@ -228,7 +228,7 @@ static int tw68_initdev(struct pci_dev *pci_dev, /* pci init */ dev->pci = pci_dev; - if (pci_enable_device(pci_dev)) { + if (pcim_enable_device(pci_dev)) { err = -EIO; goto fail1; } @@ -359,7 +359,7 @@ static void tw68_finidev(struct pci_dev *pci_dev) v4l2_device_unregister(&dev->v4l2_dev); } -static int __maybe_unused tw68_suspend(struct device *dev_d) +static int tw68_suspend(struct device *dev_d) { struct pci_dev *pci_dev = to_pci_dev(dev_d); struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); @@ -377,7 +377,7 @@ static int __maybe_unused tw68_suspend(struct device *dev_d) return 0; } -static int __maybe_unused tw68_resume(struct device *dev_d) +static int tw68_resume(struct device *dev_d) { struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d); struct tw68_dev *dev = container_of(v4l2_dev, @@ -405,14 +405,14 @@ static int __maybe_unused tw68_resume(struct device *dev_d) /* ----------------------------------------------------------- */ -static SIMPLE_DEV_PM_OPS(tw68_pm_ops, tw68_suspend, tw68_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(tw68_pm_ops, tw68_suspend, tw68_resume); static struct pci_driver tw68_pci_driver = { .name = "tw68", .id_table = tw68_pci_tbl, .probe = tw68_initdev, .remove = tw68_finidev, - .driver.pm = &tw68_pm_ops, + .driver.pm = pm_sleep_ptr(&tw68_pm_ops), }; module_pci_driver(tw68_pci_driver); |
