diff options
-rw-r--r-- | drivers/media/i2c/ov5645.c | 5 | ||||
-rw-r--r-- | drivers/media/platform/hisi/isp/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/platform/hisi/isp/isp-csiphy.c | 8 | ||||
-rw-r--r-- | drivers/media/platform/hisi/isp/isp-csiphy.h | 19 | ||||
-rw-r--r-- | drivers/media/platform/hisi/isp/isp-cvdr.c | 76 | ||||
-rw-r--r-- | drivers/media/platform/hisi/isp/isp-cvdr.h | 5 | ||||
-rw-r--r-- | drivers/media/platform/hisi/isp/isp-sr.c | 15 | ||||
-rw-r--r-- | drivers/media/platform/hisi/isp/isp-video.c | 503 | ||||
-rw-r--r-- | drivers/media/platform/hisi/isp/isp-video.h | 54 | ||||
-rw-r--r-- | drivers/media/platform/hisi/isp/isp.c | 57 | ||||
-rw-r--r-- | drivers/media/platform/hisi/isp/isp.h | 4 |
11 files changed, 670 insertions, 77 deletions
diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index d28845f7356f..2c2c2c4f9d20 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -724,11 +724,13 @@ static int ov5645_s_power(struct v4l2_subdev *sd, int on) mutex_lock(&ov5645->power_lock); + pr_info("%s: %d\n", __func__, __LINE__); /* If the power count is modified from 0 to != 0 or from != 0 to 0, * update the power state. */ if (ov5645->power_count == !on) { if (on) { + pr_info("%s: %d\n", __func__, __LINE__); ret = ov5645_set_power_on(ov5645); if (ret < 0) goto exit; @@ -750,6 +752,7 @@ static int ov5645_s_power(struct v4l2_subdev *sd, int on) goto exit; } } else { + pr_info("%s: %d\n", __func__, __LINE__); ov5645_set_power_off(ov5645); } } @@ -1054,6 +1057,7 @@ static int ov5645_s_stream(struct v4l2_subdev *subdev, int enable) struct ov5645 *ov5645 = to_ov5645(subdev); int ret; + pr_info("%s: %d\n", __func__, __LINE__); if (enable) { ret = ov5645_set_register_array(ov5645, ov5645->current_mode->data, @@ -1074,6 +1078,7 @@ static int ov5645_s_stream(struct v4l2_subdev *subdev, int enable) if (ret < 0) return ret; } else { + pr_info("%s: %d\n", __func__, __LINE__); ret = ov5645_write_reg(ov5645, OV5645_SYSTEM_CTRL0, OV5645_SYSTEM_CTRL0_STOP); if (ret < 0) diff --git a/drivers/media/platform/hisi/isp/Makefile b/drivers/media/platform/hisi/isp/Makefile index 9fa39e3a4867..99f5ba34ea23 100644 --- a/drivers/media/platform/hisi/isp/Makefile +++ b/drivers/media/platform/hisi/isp/Makefile @@ -5,6 +5,7 @@ hisi-isp-objs += \ isp.o \ isp-csiphy.o \ isp-sr.o \ + isp-video.o \ isp-cvdr.o \ obj-$(CONFIG_VIDEO_HISI_ISP) += hisi-isp.o diff --git a/drivers/media/platform/hisi/isp/isp-csiphy.c b/drivers/media/platform/hisi/isp/isp-csiphy.c index f6c050fc0814..6c3fae396d04 100644 --- a/drivers/media/platform/hisi/isp/isp-csiphy.c +++ b/drivers/media/platform/hisi/isp/isp-csiphy.c @@ -239,6 +239,7 @@ int isp_ispss_reset_all(struct isp *isp) void isp_ispss_clk_enable(struct isp *isp) { + pr_info("%s: %d\n", __func__, __LINE__); /* enable all clock of isp sub-modules */ isp_writel(isp->ispss_ctrl, 0x010, 0xffffffff); isp_writel(isp->ispss_ctrl, 0x014, 0xffffffff); @@ -263,7 +264,8 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); struct isp *isp = csiphy->isp; int ret = 0; - + + pr_info("%s: %d\n", __func__, __LINE__); ret = isp_enable_clocks(isp->clks, isp->dev); if (ret < 0) return ret; @@ -289,10 +291,12 @@ static int csiphy_set_stream(struct v4l2_subdev *sd, int enable) struct isp *isp = csiphy->isp; int ret; + pr_info("%s: %d\n", __func__, __LINE__); isp_ispss_reset_all(isp); mdelay(100); - ret = csi2if_enable(csiphy, 2, 0); + pr_info("%s: Data lane count is: %d\n", __func__, csiphy->cfg.num_data); + ret = csi2if_enable(csiphy, csiphy->cfg.num_data, 0); if (ret < 0) return ret; diff --git a/drivers/media/platform/hisi/isp/isp-csiphy.h b/drivers/media/platform/hisi/isp/isp-csiphy.h index 2382ca2de585..77941bbf6fcd 100644 --- a/drivers/media/platform/hisi/isp/isp-csiphy.h +++ b/drivers/media/platform/hisi/isp/isp-csiphy.h @@ -88,25 +88,9 @@ #define LANE3_DESKEW_2 (0x6B) #define LANE3_DESKEW_3 (0x6C) -struct csiphy_lane { - u8 pos; - u8 pol; -}; - -struct csiphy_lanes_cfg { - int num_data; - struct csiphy_lane *data; - struct csiphy_lane clk; -}; - -struct csiphy_csi2_cfg { - struct csiphy_lanes_cfg lane_cfg; -}; - struct csiphy_config { - u8 combo_mode; u8 csid_id; - struct csiphy_csi2_cfg *csi2; + int num_data; }; struct csiphy_device; @@ -146,4 +130,5 @@ int isp_csiphy_register_entity(struct csiphy_device *csiphy, void isp_csiphy_unregister_entity(struct csiphy_device *csiphy); +void isp_ispss_clk_enable(struct isp *isp); #endif /* HISI_ISP_CSIPHY_H */ diff --git a/drivers/media/platform/hisi/isp/isp-cvdr.c b/drivers/media/platform/hisi/isp/isp-cvdr.c index 666dd016028f..b6a1b2c71d70 100644 --- a/drivers/media/platform/hisi/isp/isp-cvdr.c +++ b/drivers/media/platform/hisi/isp/isp-cvdr.c @@ -180,6 +180,7 @@ void isp_cvdr_init(struct cvdr_device *cvdr) prefetch_bypass = 1; + pr_info("%s: %d\n", __func__, __LINE__); for (i = 0; i < CVDR_VP_WR_NBR; ++i) { base = ((cvdr_vp_wr_bw[i].srt == CVDR_SRT) ? cvdr->cvdr_srt : cvdr->cvdr_rt); @@ -227,8 +228,8 @@ void isp_cvdr_init(struct cvdr_device *cvdr) } /* - * cvdr_set_power - Power on/off CSIPHY module - * @sd: CSIPHY V4L2 subdevice + * cvdr_set_power - Power on/off CVDR module + * @sd: CVDR V4L2 subdevice * @on: Requested power state * * Return 0 on success or a negative error code otherwise @@ -241,8 +242,8 @@ static int cvdr_set_power(struct v4l2_subdev *sd, int on) } /* - * cvdr_set_stream - Enable/disable streaming on CSIPHY module - * @sd: CSIPHY V4L2 subdevice + * cvdr_set_stream - Enable/disable streaming on CVDR module + * @sd: CVDR V4L2 subdevice * @enable: Requested streaming state * * Return 0 on success or a negative error code otherwise @@ -253,6 +254,7 @@ static int cvdr_set_stream(struct v4l2_subdev *sd, int enable) struct isp *isp = cvdr->isp; int ret = 0; + pr_info("%s: %d\n", __func__, __LINE__); enable_irq(cvdr->irq_vic1); isp_cvdr_init(cvdr); isp_cvdr_config(cvdr, isp->hw_addr); @@ -263,7 +265,7 @@ static int cvdr_set_stream(struct v4l2_subdev *sd, int enable) /* * cvdr_init_formats - Initialize formats on all pads - * @sd: CSIPHY V4L2 subdevice + * @sd: CVDR V4L2 subdevice * @fh: V4L2 subdev file handle * * Initialize all pad formats with default values. @@ -278,10 +280,12 @@ static int cvdr_init_formats(struct v4l2_subdev *sd, irqreturn_t isp_vic1_handler(int irq, void *dev) { + pr_info("%s: %d\n", __func__, __LINE__); struct cvdr_device *cvdr = dev; struct isp *isp = cvdr->isp; unsigned int val = isp_clear_irq(isp, IRQ_MERGER_DEBUG_1); + pr_info("%s: %d\n", __func__, __LINE__); if (val & (1 << IRQ_MERGER_SR_4_CVDR_RT_SOF_VPWR_23_OFFSET)) isp_cvdr_config(cvdr, isp->hw_addr + frame_num2Offset(isp, isp->frame_num + 1)); @@ -290,10 +294,9 @@ irqreturn_t isp_vic1_handler(int irq, void *dev) } /* - * isp_cvdr_subdev_init - Initialize CSIPHY device structure and resources - * @sr: CSIPHY device - * @res: CSIPHY module resources table - * @id: CSIPHY module id + * isp_cvdr_subdev_init - Initialize CVDR device structure and resources + * @cvdr: CVDR device + * @res: CVDR module resources table * * Return 0 on success or a negative error code otherwise */ @@ -307,6 +310,8 @@ int isp_cvdr_subdev_init(struct isp *isp, int ret; cvdr->isp = isp; + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res[0].reg); cvdr->cvdr_rt = devm_ioremap_resource(dev, r); if (IS_ERR(cvdr->cvdr_rt)) { @@ -347,7 +352,7 @@ int isp_cvdr_subdev_init(struct isp *isp, } /* - * cvdr_link_setup - Setup CSIPHY connections + * cvdr_link_setup - Setup CVDR connections * @entity: Pointer to media entity structure * @local: Pointer to local pad * @remote: Pointer to remote pad @@ -397,8 +402,8 @@ static const struct media_entity_operations cvdr_media_ops = { }; /* - * isp_cvdr_register_entity - Register subdev node for CSIPHY module - * @sr: CSIPHY device + * isp_cvdr_register_entity - Register subdev node for CVDR module + * @cvdr: CVDR device * @v4l2_dev: V4L2 device * * Return 0 on success or a negative error code otherwise @@ -409,7 +414,9 @@ int isp_cvdr_register_entity(struct cvdr_device *cvdr, struct v4l2_subdev *sd = &cvdr->subdev; struct media_pad *pads = cvdr->pads; struct device *dev = cvdr->isp->dev; + struct isp_video *video_out = &cvdr->video_out; int ret; + char name[32]; v4l2_subdev_init(sd, &cvdr_v4l2_ops); sd->internal_ops = &cvdr_v4l2_internal_ops; @@ -424,12 +431,12 @@ int isp_cvdr_register_entity(struct cvdr_device *cvdr, return ret; } - pads[ISP_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK; - pads[ISP_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; + pads[ISP_CVDR_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[ISP_CVDR_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; sd->entity.function = MEDIA_ENT_F_IO_V4L; sd->entity.ops = &cvdr_media_ops; - ret = media_entity_pads_init(&sd->entity, ISP_CSIPHY_PADS_NUM, pads); + ret = media_entity_pads_init(&sd->entity, ISP_CVDR_PADS_NUM, pads); if (ret < 0) { dev_err(dev, "Failed to init media entity: %d\n", ret); return ret; @@ -438,15 +445,48 @@ int isp_cvdr_register_entity(struct cvdr_device *cvdr, ret = v4l2_device_register_subdev(v4l2_dev, sd); if (ret < 0) { dev_err(dev, "Failed to register subdev: %d\n", ret); - media_entity_cleanup(&sd->entity); + goto error_reg_subdev; + } + + video_out->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + video_out->isp = cvdr->isp; + snprintf(name, ARRAY_SIZE(name), "%s", "hisi_video"); + ret = isp_video_register(video_out, v4l2_dev, name); + if (ret < 0) { + dev_err(dev, "Failed to register video node: %d\n", + ret); + goto error_reg_video; } + pr_info("Linking entities: %s->%s\n", sd->entity.name, video_out->vdev.entity.name); + ret = media_create_pad_link( + &sd->entity, ISP_CVDR_PAD_SRC, + &video_out->vdev.entity, 0, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + if (ret < 0) { + dev_err(dev, "Failed to link %s->%s entities: %d\n", + sd->entity.name, video_out->vdev.entity.name, + ret); + goto error_link; + } + + return ret; + +error_link: + isp_video_unregister(video_out); + +error_reg_video: + v4l2_device_unregister_subdev(sd); + +error_reg_subdev: + media_entity_cleanup(&sd->entity); + return ret; } /* - * isp_cvdr_unregister_entity - Unregister CSIPHY module subdev node - * @sr: CSIPHY device + * isp_cvdr_unregister_entity - Unregister CVDR module subdev node + * @cvdr: CVDR device */ void isp_cvdr_unregister_entity(struct cvdr_device *cvdr) { diff --git a/drivers/media/platform/hisi/isp/isp-cvdr.h b/drivers/media/platform/hisi/isp/isp-cvdr.h index 8824a7c39ba1..632284e635e6 100644 --- a/drivers/media/platform/hisi/isp/isp-cvdr.h +++ b/drivers/media/platform/hisi/isp/isp-cvdr.h @@ -13,9 +13,11 @@ #include <media/v4l2-mediabus.h> #include <media/v4l2-subdev.h> +#include "isp-video.h" + #define ISP_CVDR_PAD_SINK 0 #define ISP_CVDR_PAD_SRC 1 -#define ISP_CVDR_PADS_NUM 1 +#define ISP_CVDR_PADS_NUM 2 #define CVDR_SRT_CVDR_CFG_REG 0x0 #define CVDR_RT_CVDR_CFG_REG 0x0 @@ -937,6 +939,7 @@ struct cvdr_device { void __iomem *cvdr_srt; void __iomem *sub_ctrl; unsigned int irq_vic1; + struct isp_video video_out; }; int isp_cvdr_subdev_init(struct isp *isp, diff --git a/drivers/media/platform/hisi/isp/isp-sr.c b/drivers/media/platform/hisi/isp/isp-sr.c index 14f1ac4d4cef..e34b6baaf99f 100644 --- a/drivers/media/platform/hisi/isp/isp-sr.c +++ b/drivers/media/platform/hisi/isp/isp-sr.c @@ -22,16 +22,18 @@ void isp_sr_config(struct sr_device *sr) { + struct isp *isp = sr->isp; int data_type = 1; union U_ID_ROUTER_1 reg1; union U_REFORMAT reg; + pr_info("%s: %d\n", __func__, __LINE__); isp_writel(sr->base, STREAM_ROUTER_CSIFILTER_A_REG, (0 << 6) | (YUV_DT_422_8BITS << 0)); reg1.u32 = isp_readl(sr->base, STREAM_ROUTER_ID_ROUTER_1_REG); reg1.bits.idr_enable_6 = 1; - reg1.bits.idr_input_stream_6 = (0 << 2) | (0 << 0); + reg1.bits.idr_input_stream_6 = (isp->csiphy_id << 2) | (0 << 0); isp_writel(sr->base, STREAM_ROUTER_ID_ROUTER_1_REG, reg1.u32); reg.u32 = 0; @@ -46,6 +48,7 @@ void isp_sr_config(struct sr_device *sr) void isp_sr_go(struct sr_device *sr, unsigned int go_bit) { + pr_info("%s: %d\n", __func__, __LINE__); isp_writel(sr->base, STREAM_ROUTER_CSIFILTER_GO_REG, go_bit); } @@ -75,6 +78,7 @@ static int sr_set_stream(struct v4l2_subdev *sd, int enable) struct sr_device *sr = v4l2_get_subdevdata(sd); int ret = 0; + pr_info("%s: %d\n", __func__, __LINE__); enable_irq(sr->irq_frproc0); isp_config_smmu_bypass(sr->isp); isp_sr_config(sr); @@ -102,13 +106,14 @@ static int sr_init_formats(struct v4l2_subdev *sd, irqreturn_t isp_frproc0_handler(int irq, void *dev) { + pr_info("%s: %d\n", __func__, __LINE__); struct sr_device *sr = dev; struct isp *isp = sr->isp; unsigned int val = isp_clear_irq(sr->isp, IRQ_MERGER_FRPROC_0); if (val & (1 << IRQ_MERGER_SR_4_CVDR_RT_EOF_VPWR_23_OFFSET)) { // FIXME: Use CSI index - isp_sr_go(sr, (1 << 0)); + isp_sr_go(sr, (1 << (4 * isp->csiphy_id))); isp->frame_num++; } @@ -230,12 +235,6 @@ int isp_sr_register_entity(struct sr_device *sr, ISP_SR_NAME); v4l2_set_subdevdata(sd, sr); - ret = sr_init_formats(sd, NULL); - if (ret < 0) { - dev_err(dev, "Failed to init format: %d\n", ret); - return ret; - } - pads[ISP_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK; pads[ISP_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; diff --git a/drivers/media/platform/hisi/isp/isp-video.c b/drivers/media/platform/hisi/isp/isp-video.c new file mode 100644 index 000000000000..6ce04d3e7767 --- /dev/null +++ b/drivers/media/platform/hisi/isp/isp-video.c @@ -0,0 +1,503 @@ +#include <linux/slab.h> +#include <media/media-entity.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mc.h> +#include <media/videobuf2-dma-sg.h> + +#include "isp-video.h" +#include "isp.h" + +/* + * struct isp_format_info - ISP media bus format information + * @code: V4L2 media bus format code + * @pixelformat: V4L2 pixel format FCC identifier + * @planes: Number of planes + * @hsub: Horizontal subsampling (for each plane) + * @vsub: Vertical subsampling (for each plane) + * @bpp: Bits per pixel when stored in memory (for each plane) + */ +struct isp_format_info { + char *name; + u32 fourcc; + u8 depth; + u8 ybpp; +}; + +static const struct isp_format_info dummy_format[] = { + { + .name = "YUV 4:2:2 planar, Y/Cb/Cr", + .fourcc = V4L2_PIX_FMT_YUV422P, + .depth = 16, + .ybpp = 1, + }, +}; + +/* ----------------------------------------------------------------------------- + * Helper functions + */ + +static struct v4l2_subdev *video_remote_subdev(struct isp_video *video, + u32 *pad) +{ + struct media_pad *remote; + + pr_info("%s: %d\n", __func__, __LINE__); + remote = media_entity_remote_pad(&video->pad); + + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) + return NULL; + + if (pad) + *pad = remote->index; + + return media_entity_to_v4l2_subdev(remote->entity); +} + +/* ----------------------------------------------------------------------------- + * Video queue operations + */ + +static int video_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct isp_video *video = vb2_get_drv_priv(q); + struct isp_format_info *fmt = video->formats; + const struct v4l2_pix_format *pix = &video->active_fmt.fmt.pix; + + pr_info("%s: %d\n", __func__, __LINE__); + *num_planes = 1; + + sizes[0] = (pix->width * pix->height * fmt->depth) / 8; + + return 0; +} + +static int video_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct isp_video *video = vb2_get_drv_priv(vb->vb2_queue); + const struct v4l2_pix_format *pix = &video->active_fmt.fmt.pix; + + pr_info("%s: %d\n", __func__, __LINE__); + vb2_set_plane_payload(vb, 0, video->payload); + + vbuf->field = V4L2_FIELD_NONE; + + return 0; +} + +static void video_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct isp_video *video = vb2_get_drv_priv(vb->vb2_queue); + struct isp_buffer *buffer = container_of(vbuf, struct isp_buffer, + vb); + pr_info("%s: %d\n", __func__, __LINE__); + /* TODO: Push the buffer to hardware */ +} + +static int video_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct isp_video *video = vb2_get_drv_priv(q); + struct video_device *vdev = &video->vdev; + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + int ret; + + pr_info("%s: %d\n", __func__, __LINE__); + ret = media_pipeline_start(&vdev->entity, &video->pipe); + if (ret < 0) + return ret; + + pr_info("%s: %d\n", __func__, __LINE__); + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_entity_remote_pad(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + goto error; + } + + pr_info("%s: %d\n", __func__, __LINE__); + return 0; + +error: + media_pipeline_stop(&vdev->entity); + + video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED); + + return ret; +} + +static void video_stop_streaming(struct vb2_queue *q) +{ + struct isp_video *video = vb2_get_drv_priv(q); + struct video_device *vdev = &video->vdev; + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_entity_remote_pad(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + v4l2_subdev_call(subdev, video, s_stream, 0); + } + + media_pipeline_stop(&vdev->entity); + + video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR); +} + +static const struct vb2_ops isp_video_vb2_q_ops = { + .queue_setup = video_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_prepare = video_buf_prepare, + .buf_queue = video_buf_queue, + .start_streaming = video_start_streaming, + .stop_streaming = video_stop_streaming, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 ioctls + */ + +static int video_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct isp_video *video = video_drvdata(file); + + strlcpy(cap->driver, "hisi-isp", sizeof(cap->driver)); + strlcpy(cap->card, "HiSilicon ISP", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(video->isp->dev)); + + return 0; +} + +static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct isp_video *video = video_drvdata(file); + + f->pixelformat = video->formats->fourcc; + + return 0; +} + +static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct isp_video *video = video_drvdata(file); + struct isp_format_info *fmt = video->formats; + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->bytesperline = pix->width * fmt->ybpp; + pix->sizeimage = (pix->width * pix->height * fmt->depth) / 8; + + pix->pixelformat = fmt->fourcc; + pix->width = pix->width; + pix->height = pix->height; + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_JPEG; + + *f = video->active_fmt; + + return 0; +} + +static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct isp_video *video = video_drvdata(file); + struct v4l2_pix_format *pix = &f->fmt.pix; + + /* TODO: Evaluate and set frame format from userspace */ + video->payload = pix->sizeimage; + video->active_fmt = *f; + + pr_info("Payload size set to: %d\n", video->payload); + + return 0; +} + +static int video_enum_input(struct file *file, void *fh, + struct v4l2_input *input) +{ + if (input->index > 0) + return -EINVAL; + + strlcpy(input->name, "camera", sizeof(input->name)); + input->type = V4L2_INPUT_TYPE_CAMERA; + + return 0; +} + +static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct isp_video *video = video_drvdata(file); + + return 0; +} + +static int video_g_input(struct file *file, void *fh, unsigned int *input) +{ + *input = 0; + + return 0; +} + +static int video_s_input(struct file *file, void *fh, unsigned int input) +{ + return input == 0 ? 0 : -EINVAL; +} + +static const struct v4l2_ioctl_ops isp_vid_ioctl_ops = { + .vidioc_querycap = video_querycap, + .vidioc_enum_fmt_vid_cap_mplane = video_enum_fmt, + .vidioc_g_fmt_vid_cap_mplane = video_g_fmt, + .vidioc_s_fmt_vid_cap_mplane = video_s_fmt, + .vidioc_try_fmt_vid_cap_mplane = video_try_fmt, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_input = video_enum_input, + .vidioc_g_input = video_g_input, + .vidioc_s_input = video_s_input, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 file operations + */ + +static int video_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct isp_video *video = video_drvdata(file); + struct v4l2_fh *vfh; + int ret; + + mutex_lock(&video->lock); + + vfh = kzalloc(sizeof(*vfh), GFP_KERNEL); + if (vfh == NULL) { + ret = -ENOMEM; + goto error_alloc; + } + + v4l2_fh_init(vfh, vdev); + v4l2_fh_add(vfh); + + file->private_data = vfh; + + ret = v4l2_pipeline_pm_use(&vdev->entity, 1); + if (ret < 0) { + dev_err(video->isp->dev, "Failed to power up pipeline: %d\n", + ret); + goto error_pm_use; + } + + mutex_unlock(&video->lock); + + return 0; + +error_pm_use: + v4l2_fh_release(file); + +error_alloc: + mutex_unlock(&video->lock); + + return ret; +} + +static int video_release(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + + vb2_fop_release(file); + + v4l2_pipeline_pm_use(&vdev->entity, 0); + + file->private_data = NULL; + + return 0; +} + +static const struct v4l2_file_operations isp_vid_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = video_open, + .release = video_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .read = vb2_fop_read, +}; + +/* ----------------------------------------------------------------------------- + * CAMSS video core + */ + +static void isp_video_release(struct video_device *vdev) +{ + struct isp_video *video = video_get_drvdata(vdev); + + media_entity_cleanup(&vdev->entity); + + mutex_destroy(&video->q_lock); + mutex_destroy(&video->lock); +} + +/* + * isp_video_init_format - Helper function to initialize format + * @video: struct isp_video + * + * Initialize pad format with default value. + * + * Return 0 on success or a negative error code otherwise + */ +static int isp_video_init_format(struct isp_video *video) +{ + int ret; + struct v4l2_format format = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .fmt.pix = { + .width = 1920, + .height = 1080, + .pixelformat = V4L2_PIX_FMT_YUV422P, + }, + }; + + video->active_fmt = format; + + return 0; +} + +/* + * isp_video_register - Register a video device node + * @video: struct isp_video + * @v4l2_dev: V4L2 device + * @name: name to be used for the video device node + * + * Initialize and register a video device node to a V4L2 device. Also + * initialize the vb2 queue. + * + * Return 0 on success or a negative error code otherwise + */ + +int isp_video_register(struct isp_video *video, struct v4l2_device *v4l2_dev, + const char *name) +{ + struct media_pad *pad = &video->pad; + struct video_device *vdev; + struct vb2_queue *q; + int ret; + + vdev = &video->vdev; + + mutex_init(&video->q_lock); + + q = &video->vb2_q; + q->drv_priv = video; + q->mem_ops = &vb2_dma_sg_memops; + q->ops = &isp_video_vb2_q_ops; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + q->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->buf_struct_size = sizeof(struct isp_buffer); + q->dev = video->isp->dev; + q->lock = &video->q_lock; + ret = vb2_queue_init(q); + if (ret < 0) { + dev_err(v4l2_dev->dev, "Failed to init vb2 queue: %d\n", ret); + goto error_vb2_init; + } + + pad->flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vdev->entity, 1, pad); + if (ret < 0) { + dev_err(v4l2_dev->dev, "Failed to init video entity: %d\n", + ret); + goto error_media_init; + } + + mutex_init(&video->lock); + + video->formats = dummy_format; + video->nformats = ARRAY_SIZE(dummy_format); + + ret = isp_video_init_format(video); + if (ret < 0) { + dev_err(v4l2_dev->dev, "Failed to init format: %d\n", ret); + goto error_video_register; + } + + vdev->fops = &isp_vid_fops; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + vdev->ioctl_ops = &isp_vid_ioctl_ops; + vdev->release = isp_video_release; + vdev->v4l2_dev = v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + vdev->queue = &video->vb2_q; + vdev->lock = &video->lock; + strlcpy(vdev->name, name, sizeof(vdev->name)); + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + dev_err(v4l2_dev->dev, "Failed to register video device: %d\n", + ret); + goto error_video_register; + } + + video_set_drvdata(vdev, video); + + return 0; + +error_video_register: + media_entity_cleanup(&vdev->entity); + mutex_destroy(&video->lock); +error_media_init: + vb2_queue_release(&video->vb2_q); +error_vb2_init: + mutex_destroy(&video->q_lock); + + return ret; +} + +void isp_video_stop_streaming(struct isp_video *video) +{ + if (vb2_is_streaming(&video->vb2_q)) + vb2_queue_release(&video->vb2_q); +} + +void isp_video_unregister(struct isp_video *video) +{ + video_unregister_device(&video->vdev); +} diff --git a/drivers/media/platform/hisi/isp/isp-video.h b/drivers/media/platform/hisi/isp/isp-video.h new file mode 100644 index 000000000000..1ca7015f1da3 --- /dev/null +++ b/drivers/media/platform/hisi/isp/isp-video.h @@ -0,0 +1,54 @@ +#ifndef ISP_VIDEO_H +#define ISP_VIDEO_H + +#include <linux/mutex.h> +#include <linux/videodev2.h> +#include <media/media-entity.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-mediabus.h> +#include <media/videobuf2-v4l2.h> + +struct isp_buffer { + struct vb2_v4l2_buffer vb; + dma_addr_t addr[3]; + struct list_head queue; +}; + +struct isp_video; + +struct isp_video_ops { + int (*queue_buffer)(struct isp_video *vid, struct isp_buffer *buf); + int (*flush_buffers)(struct isp_video *vid, + enum vb2_buffer_state state); +}; + +struct isp_format_info; + +struct isp_video { + struct isp *isp; + struct vb2_queue vb2_q; + struct video_device vdev; + struct media_pad pad; + struct v4l2_format active_fmt; + enum v4l2_buf_type type; + struct media_pipeline pipe; + const struct isp_video_ops *ops; + struct mutex lock; + struct mutex q_lock; + unsigned int bpl_alignment; + unsigned int line_based; + unsigned int nformats; + unsigned int payload; + const struct isp_format_info *formats; +}; + +void isp_video_stop_streaming(struct isp_video *video); + +int isp_video_register(struct isp_video *video, struct v4l2_device *v4l2_dev, + const char *name); + +void isp_video_unregister(struct isp_video *video); + +#endif /* ISP_VIDEO_H */ diff --git a/drivers/media/platform/hisi/isp/isp.c b/drivers/media/platform/hisi/isp/isp.c index f71fbe6fbaf2..7b0cceee74ca 100644 --- a/drivers/media/platform/hisi/isp/isp.c +++ b/drivers/media/platform/hisi/isp/isp.c @@ -96,17 +96,20 @@ int frame_num2Offset(struct isp *isp, int frame_num) void isp_config_smmu_bypass(struct isp *isp) { + pr_info("%s: %d\n", __func__, __LINE__); isp_writel(isp->smmu_ctrl, 0x0, 0x1); } void isp_ispss_enable_irq(struct isp *isp) { + pr_info("%s: %d\n", __func__, __LINE__); isp_writel(isp->irq_merger2, IRQ_MERGER_IMSC_DEBUG1, 0xFFFFFFFF); isp_writel(isp->irq_merger2, IRQ_MERGER_IMSC_FRPROC0, 0xFFFFFFFF); } void isp_ispss_clear_irq_state(struct isp *isp) { + pr_info("%s: %d\n", __func__, __LINE__); isp_writel(isp->irq_merger2, IRQ_MERGER_ICR_DEBUG0, 0xFFFFFFFF); isp_writel(isp->irq_merger2, IRQ_MERGER_ICR_DEBUG1, 0xFFFFFFFF); isp_writel(isp->irq_merger2, IRQ_MERGER_ICR_DEBUG2, 0xFFFFFFFF); @@ -226,32 +229,25 @@ int isp_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock) */ static int isp_of_parse_endpoint_node(struct device *dev, struct device_node *node, - struct isp_async_subdev *csd) + struct isp_async_subdev *csd, + struct v4l2_async_notifier *async) { - struct csiphy_lanes_cfg *lncfg = &csd->interface.csi2.lane_cfg; + struct csiphy_config *cfg = &csd->interface.cfg; struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2; - struct v4l2_fwnode_endpoint vep = { { 0 } }; + struct v4l2_fwnode_endpoint vep = { .bus_type = 0 }; + struct isp *isp = container_of(async, struct isp, notifier); unsigned int i; v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep); - csd->interface.csiphy_id = vep.base.port; + /* CSI2PHY port id */ + isp->csiphy_id = vep.base.port; + pr_info("CSIPHY ID: %d\n", isp->csiphy_id); mipi_csi2 = &vep.bus.mipi_csi2; - lncfg->clk.pos = mipi_csi2->clock_lane; - lncfg->clk.pol = mipi_csi2->lane_polarities[0]; - lncfg->num_data = mipi_csi2->num_data_lanes; - - lncfg->data = devm_kcalloc(dev, - lncfg->num_data, sizeof(*lncfg->data), - GFP_KERNEL); - if (!lncfg->data) - return -ENOMEM; - for (i = 0; i < lncfg->num_data; i++) { - lncfg->data[i].pos = mipi_csi2->data_lanes[i]; - lncfg->data[i].pol = mipi_csi2->lane_polarities[i + 1]; - } + /* CSI2PHY data lanes */ + cfg->num_data = mipi_csi2->num_data_lanes; return 0; } @@ -299,7 +295,7 @@ static int isp_of_parse_ports(struct device *dev, notifier->subdevs[i++] = &csd->asd; - ret = isp_of_parse_endpoint_node(dev, node, csd); + ret = isp_of_parse_endpoint_node(dev, node, csd, notifier); if (ret < 0) { of_node_put(node); return ret; @@ -453,10 +449,11 @@ static int isp_subdev_notifier_bound(struct v4l2_async_notifier *async, struct isp *isp = container_of(async, struct isp, notifier); struct isp_async_subdev *csd = container_of(asd, struct isp_async_subdev, asd); - u8 id = csd->interface.csiphy_id; + u8 id = isp->csiphy_id; struct csiphy_device *csiphy = &isp->csiphy[id]; - csiphy->cfg.csi2 = &csd->interface.csi2; + pr_info("Notifier bound CSIPHY id: %d\n", id); + csiphy->cfg.num_data = csd->interface.cfg.num_data; subdev->host_priv = csiphy; return 0; @@ -469,6 +466,7 @@ static int isp_subdev_notifier_complete(struct v4l2_async_notifier *async) struct v4l2_subdev *sd; int ret; + pr_info("Notifier complete\n"); list_for_each_entry(sd, &v4l2_dev->subdevs, list) { if (sd->host_priv) { struct media_entity *sensor = &sd->entity; @@ -594,31 +592,26 @@ static int isp_probe(struct platform_device *pdev) if (ret < 0) goto err_cleanup; - isp->virt_addr = dma_alloc_coherent(dev, isp->pool_size, - &(isp->hw_addr), GFP_KERNEL); - if (!isp->virt_addr) { - pr_err("dma_alloc_coherent (ISP) alloc err!\n"); - ret = -ENOMEM; - goto err_cleanup; - } - - memset(isp->virt_addr, 0, isp->pool_size); + pr_info("%s: %d\n", __func__, __LINE__); ret = isp_of_parse_ports(dev, &isp->notifier); if (ret < 0) { goto err_cleanup; } + pr_info("%s: %d\n", __func__, __LINE__); ret = isp_init_subdevices(isp); if (ret < 0) goto err_cleanup; + pr_info("%s: %d\n", __func__, __LINE__); isp->media_dev.dev = isp->dev; strlcpy(isp->media_dev.model, "HiSilicon ISP", sizeof(isp->media_dev.model)); isp->media_dev.ops = &isp_media_ops; media_device_init(&isp->media_dev); + pr_info("%s: %d\n", __func__, __LINE__); /* FIXME: Get these from userspace */ isp->frame_size = 0x3F4800; isp->frame_count = 4; @@ -630,11 +623,15 @@ static int isp_probe(struct platform_device *pdev) goto err_cleanup; } + pr_info("%s: %d\n", __func__, __LINE__); ret = isp_register_entities(isp); if (ret < 0) goto err_register_entities; + pr_info("%s: %d\n", __func__, __LINE__); + if (isp->notifier.num_subdevs) { + pr_info("%s: %d\n", __func__, __LINE__); isp->notifier.ops = &isp_subdev_notifier_ops; ret = v4l2_async_notifier_register(&isp->v4l2_dev, @@ -646,6 +643,7 @@ static int isp_probe(struct platform_device *pdev) goto err_register_subdevs; } } else { + pr_info("%s: %d\n", __func__, __LINE__); ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev); if (ret < 0) { dev_err(dev, "Failed to register subdev nodes: %d\n", @@ -661,6 +659,7 @@ static int isp_probe(struct platform_device *pdev) } } + pr_info("%s: %d\n", __func__, __LINE__); return 0; err_register_subdevs: diff --git a/drivers/media/platform/hisi/isp/isp.h b/drivers/media/platform/hisi/isp/isp.h index dc9aa590d615..2803e23e4d1e 100644 --- a/drivers/media/platform/hisi/isp/isp.h +++ b/drivers/media/platform/hisi/isp/isp.h @@ -130,6 +130,7 @@ struct isp { void *virt_addr; dma_addr_t hw_addr; int csiphy_num; + u8 csiphy_id; unsigned int pool_size; unsigned int frame_size; unsigned int frame_count; @@ -137,8 +138,7 @@ struct isp { }; struct isp_camera_interface { - u8 csiphy_id; - struct csiphy_csi2_cfg csi2; + struct csiphy_config cfg; }; struct isp_async_subdev { |