// SPDX-License-Identifier: GPL-2.0+ #include "spi_camera.h" #define SPI_SENSOR_NAME "gc032a" static int spi_sensor_open(struct file *file) { struct spi_sensor *gc032a = video_drvdata(file); pr_info("%s\n", __func__); set_bit(DRIVER_BUSY, &gc032a->state); return 0; } static int spi_sensor_close(struct file *file) { struct spi_sensor *gc032a = video_drvdata(file); int ret = 0; pr_info("%s\n", __func__); if (!driver_busy(gc032a)) { pr_err("%s: video have been closed!\n", __func__); return 0; } if (driver_streaming(gc032a)) { disable_irq(gc032a->cb_irq); //wait all work done before streamoff flush_work(&gc032a->spi_rx_work); ret = vb2_ioctl_streamoff(file, NULL, V4L2_CAP_VIDEO_CAPTURE_MPLANE); if (ret != 0) pr_err("%s: vb2_ioctl_streamoff error\n", __func__); clear_bit(DRIVER_STREAM, &gc032a->state); } v4l2_subdev_call(gc032a->sd, core, s_power, PWR_OFF); clear_bit(DRIVER_BUSY, &gc032a->state); return 0; } static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) { struct spi_sensor *gc032a = vb2_get_drv_priv(vq); struct spi_device *spi = (struct spi_device *)gc032a->spidev; static u64 dma_mask = DMA_BIT_MASK(32); int size; *nplanes = 1; //YUV422, head and tail size = gc032a->height * gc032a->width * 2 + (gc032a->frame_unit_size * 2) + ((gc032a->line_unit_size * 2) * gc032a->height); //gc032a frame data contains YUV422 data/frmae head/tail/due and line head/tail sizes[0] = size; if (sizes[0] == 0) { pr_info("%s size not right for YUV422\n", __func__); //sizes[0] = 640 * 480 * 2; sizes[0] = size; } spi->dev.dma_mask = &dma_mask; spi->dev.coherent_dma_mask = DMA_BIT_MASK(32); alloc_devs[0] = &spi->dev; return 0; } static int buffer_prepare(struct vb2_buffer *vb) { struct spi_sensor *gc032a = vb2_get_drv_priv(vb->vb2_queue); struct vb2_v4l2_buffer *vvb = container_of(vb, struct vb2_v4l2_buffer, vb2_buf); struct spi_buffer *buf = container_of(vvb, struct spi_buffer, vb); unsigned long size; //gc032a frame data contains YUV422 data/frmae head/tail and line head/tail size = gc032a->height * gc032a->width * 2 + (gc032a->frame_unit_size * 2) + ((gc032a->line_unit_size * 2) * gc032a->height); vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); vb->planes[0].m.offset = vb2_dma_contig_plane_dma_addr(vb, 0); return 0; } static void buffer_queue(struct vb2_buffer *vb) { struct spi_sensor *gc032a = vb2_get_drv_priv(vb->vb2_queue); struct vb2_v4l2_buffer *vvb = container_of(vb, struct vb2_v4l2_buffer, vb2_buf); struct spi_buffer *buf = container_of(vvb, struct spi_buffer, vb); struct spi_dmaqueue *vidq = &gc032a->vidq; spin_lock(&gc032a->slock); list_add_tail(&buf->list, &vidq->active); spin_unlock(&gc032a->slock); } static void stop_streaming(struct vb2_queue *vq) { struct spi_sensor *gc032a = vb2_get_drv_priv(vq); struct spi_dmaqueue *dma_q = &gc032a->vidq; unsigned long flags = 0; spin_lock_irqsave(&gc032a->slock, flags); /* Release all active buffers*/ while (!list_empty(&dma_q->active)) { struct spi_buffer *spi_buf; spi_buf = list_entry(dma_q->active.next, struct spi_buffer, list); list_del(&spi_buf->list); vb2_buffer_done(&spi_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); pr_info("stop streaming buffer %d stop\n", spi_buf->vb.vb2_buf.index); } spin_unlock_irqrestore(&gc032a->slock, flags); } static void strip_buffer(struct spi_sensor *gc032a, void *start, int *length) { char *front = (char *)start; char *behind = (char *)start; const int FRAME_UNIT = gc032a->frame_unit_size; //frame head and tail size const int LINE_UNIT = gc032a->line_unit_size; //line head and tail size const int WIDTH = gc032a->width; const int HEIGHT = gc032a->height; int LINE_DATA_LENGTH = WIDTH * 2; //YUV422 int data_length = 0; int i; //front prt skip frame head front = front + FRAME_UNIT; for (i = 0; i < HEIGHT; i++) { front = front + LINE_UNIT; //skip line head strncpy(behind, front, LINE_DATA_LENGTH); front += LINE_DATA_LENGTH; behind += LINE_DATA_LENGTH; data_length += LINE_DATA_LENGTH; front = front + LINE_UNIT; //skip line end } front = front + FRAME_UNIT; *length = data_length; } static void buffer_finish(struct vb2_buffer *vb) { struct spi_sensor *gc032a = vb2_get_drv_priv(vb->vb2_queue); void *buffer = vb2_plane_vaddr(vb, 0); int length = 0; if (gc032a->strip_buffer) { strip_buffer(gc032a, buffer, &length); vb2_set_plane_payload(vb, 0, length); } } static struct vb2_ops spi_video_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, .stop_streaming = stop_streaming, .buf_finish = buffer_finish, }; /* * IOCTL vidioc handling */ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct spi_sensor *gc032a = video_drvdata(file); strlcpy(cap->driver, "spi-sensor", sizeof(cap->driver)); strlcpy(cap->card, "spi-sensor", sizeof(cap->card)); strlcpy(cap->bus_info, gc032a->v4l2_dev.name, sizeof(cap->bus_info)); cap->version = 1; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | V4L2_CAP_DEVICE_CAPS; cap->device_caps |= V4L2_CAP_EXT_PIX_FORMAT; return 0; } static int vidioc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { struct spi_sensor *gc032a = video_drvdata(file); struct v4l2_subdev_frame_size_enum fse; int ret; if (!gc032a || !gc032a->sd->ops->pad->enum_frame_size) return -EINVAL; fse.index = fsize->index; ret = v4l2_subdev_call(gc032a->sd, pad, enum_frame_size, NULL, &fse); if (ret >= 0) { fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; fsize->discrete.width = fse.max_height; fsize->discrete.height = fse.max_width; } return ret; } static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { struct spi_sensor *gc032a = video_drvdata(file); int ret = 0; ret = v4l2_subdev_call(gc032a->sd, core, s_power, PWR_ON); pr_info("%s: ################ sensor power_on______________________\n", __func__); ret = v4l2_subdev_call(gc032a->sd, core, init, 0); return ret; } static int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *parms) { struct spi_sensor *gc032a = video_drvdata(file); int ret = 0; pr_err("enter %s\n", __func__); if (parms->parm.capture.capturemode != V4L2_MODE_VIDEO && parms->parm.capture.capturemode != V4L2_MODE_IMAGE && parms->parm.capture.capturemode != V4L2_MODE_PREVIEW) { parms->parm.capture.capturemode = V4L2_MODE_PREVIEW; } ret = v4l2_subdev_call(gc032a->sd, video, s_parm, parms); if (ret < 0) pr_err("v4l2 sub device s_parm error!\n"); return ret; } static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pixm; pixm = &f->fmt.pix_mp; pixm->field = V4L2_FIELD_NONE; pixm->num_planes = 1; return 0; } static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct spi_sensor *gc032a = video_drvdata(file); struct v4l2_subdev_format format; int ret; format.format.width = f->fmt.pix.width; format.format.height = f->fmt.pix.height; ret = v4l2_subdev_call(gc032a->sd, pad, set_fmt, NULL, &format); gc032a->width = format.format.width; gc032a->height = format.format.height; gc032a->code = format.format.code; f->fmt.pix.width = format.format.width; f->fmt.pix.height = format.format.height; pr_info("%s get width:%d, height:%d\n", __func__, gc032a->width, gc032a->height); return ret; } static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { struct spi_sensor *gc032a = video_drvdata(file); struct spi_dmaqueue *dma_q = &gc032a->vidq; int ret = 0; vb2_ioctl_streamon(file, priv, i); if (driver_streaming(gc032a)) { pr_err("%s: video has already stream on\n", __func__); return -EBUSY; } set_bit(DRIVER_STREAM, &gc032a->state); if (!list_empty(&dma_q->active)) { /* need to queue buffer first before stream on */ schedule_work(&gc032a->spi_rx_work); /* spi rx data */ usleep_range(10000, 12000); ret = v4l2_subdev_call(gc032a->sd, video, s_stream, 1); } else { pr_info("enter %s, dma_q->active is empty\n", __func__); } //enable dma callback irq enable_irq(gc032a->cb_irq); return 0; } static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) { struct spi_sensor *gc032a = video_drvdata(file); int ret = 0; if (!driver_streaming(gc032a)) { pr_err("%s: video already streamoff\n", __func__); return -1; } disable_irq(gc032a->cb_irq); //wait all work done before streamoff flush_work(&gc032a->spi_rx_work); //videobuf_streamoff(&gc032a->vb_vidq); ret = vb2_ioctl_streamoff(file, priv, i); if (ret != 0) pr_err("%s vb2_ioctl_streamoff error\n", __func__); clear_bit(DRIVER_STREAM, &gc032a->state); return 0; } static const struct v4l2_file_operations spi_sensor_fops = { .owner = THIS_MODULE, .open = spi_sensor_open, .release = spi_sensor_close, .unlocked_ioctl = video_ioctl2, .mmap = vb2_fop_mmap, .poll = vb2_fop_poll, }; static const struct v4l2_ioctl_ops spi_sensor_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_s_input = vidioc_s_input, .vidioc_s_parm = vidioc_s_parm, .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap, .vidioc_reqbufs = vb2_ioctl_reqbufs, .vidioc_querybuf = vb2_ioctl_querybuf, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_qbuf = vb2_ioctl_qbuf, .vidioc_dqbuf = vb2_ioctl_dqbuf, }; unsigned int os_gpio_request(struct gpio_config *pin_cfg) { unsigned int ret = 0; if (pin_cfg->gpio == GPIO_INDEX_INVALID) return 0; ret = gpio_request(pin_cfg->gpio, NULL); if (ret != 0) { pr_err("%s failed, gpio=%d, ret=0x%x\n", __func__, pin_cfg->gpio, ret); return 0; } return pin_cfg->gpio; } int os_gpio_release(unsigned int p_handler) { gpio_free(p_handler); return 0; } int os_gpio_set(struct gpio_config *pin_cfg) { int ret = 0; char pin_name[32]; __u32 config; if (pin_cfg->gpio == GPIO_INDEX_INVALID) return 0; /* valid pin of sunxi-pinctrl, * config pin attributes individually. */ sunxi_gpio_to_name(pin_cfg->gpio, pin_name); config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, pin_cfg->mul_sel); pin_config_set(SUNXI_PINCTRL, pin_name, config); if (pin_cfg->pull != GPIO_PULL_DEFAULT) { config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD, pin_cfg->pull); pin_config_set(SUNXI_PINCTRL, pin_name, config); } if (pin_cfg->drv_level != GPIO_DRVLVL_DEFAULT) { config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DRV, pin_cfg->drv_level); pin_config_set(SUNXI_PINCTRL, pin_name, config); } if (pin_cfg->data != GPIO_DATA_DEFAULT) { config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT, pin_cfg->data); pin_config_set(SUNXI_PINCTRL, pin_name, config); } return ret; } static irqreturn_t spi_sensor_isr(int irq, void *dev_id) { struct spi_sensor *gc032a = (struct spi_sensor *)dev_id; struct spi_dmaqueue *dma_q = &gc032a->vidq; if (!list_empty(&dma_q->active)) { /* spi_sync */ schedule_work(&gc032a->spi_rx_work); /* spi rx data */ } else { pr_err("dma_q->active is empty\n"); } return IRQ_HANDLED; } static void spi_rx_work_handle(struct work_struct *work) { struct spi_sensor *gc032a = container_of(work, struct spi_sensor, spi_rx_work); struct spi_device *spi = (struct spi_device *)gc032a->spidev; struct spi_dmaqueue *dma_q = &gc032a->vidq; struct spi_transfer transfer; struct spi_message message; struct spi_buffer *spi_buf; int ret; mutex_lock(&gc032a->spi_rx_mutex); //fetch buffer spi_buf = list_entry(dma_q->active.next, struct spi_buffer, list); //configure message and transfer memset(&transfer, 0, sizeof(transfer)); transfer.rx_buf = vb2_plane_vaddr(&spi_buf->vb.vb2_buf, 0); transfer.rx_dma = vb2_dma_contig_plane_dma_addr(&spi_buf->vb.vb2_buf, 0); transfer.len = spi_buf->vb.vb2_buf.planes[0].bytesused; #if defined SPI_4_WIRE transfer.rx_nbits = SPI_NBITS_QUAD; #elif defined SPI_2_WIRE transfer.rx_nbits = SPI_NBITS_DUAL; #endif memset(&message, 0, sizeof(message)); spi_message_init(&message); message.is_dma_mapped = 1; spi_message_add_tail(&transfer, &message); ret = spi_sync(spi, &message); if (ret < 0) pr_err("%s: spi_sync return error %d\n", __func__, ret); if (&dma_q->active != dma_q->active.next->next) { spi_buf = list_entry(dma_q->active.next, struct spi_buffer, list); list_del(&spi_buf->list); vb2_buffer_done(&spi_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } else { pr_info("enter %s, only one buffer left for active\n", __func__); } mutex_unlock(&gc032a->spi_rx_mutex); } static int spi_sensor_probe(struct spi_device *spi) { struct device_node *np = spi->dev.of_node; struct spi_sensor *gc032a; struct i2c_adapter *i2c_adap; struct v4l2_device *v4l2_dev; struct video_device *vdev; struct vb2_queue *q; int ret = 0; gc032a = kzalloc(sizeof(*gc032a), GFP_KERNEL); memset(gc032a, 0, sizeof(*gc032a)); //i2c ret = of_property_read_u32(np, "sensor_twi_id", &gc032a->twi_id); if (ret) { gc032a->twi_id = 0; pr_err("fetch sensor_twi_id from device_tree failed\n"); } ret = of_property_read_u32(np, "sensor_twi_addr", &gc032a->twi_addr); if (ret) { gc032a->twi_addr = 0; pr_err("fetch sensor_twi_addr from device_tree failed\n"); } //sensor_pwdn gc032a->sensor_pwdn.gpio = of_get_named_gpio_flags(np, "sensor_pwdn", 0, (enum of_gpio_flags *)(&gc032a->sensor_pwdn)); if (!gpio_is_valid(gc032a->sensor_pwdn.gpio)) pr_err("%s: sensor_pwdn is invalid.\n", __func__); //sensor_cs gc032a->sensor_cs.gpio = of_get_named_gpio_flags(np, "sensor_cs", 0, (enum of_gpio_flags *)(&gc032a->sensor_cs)); if (!gpio_is_valid(gc032a->sensor_cs.gpio)) pr_err("%s: sensor_cs is invalid.\n", __func__); //cb interrupt gc032a->sensor_cb.gpio = of_get_named_gpio_flags(np, "sensor_cb", 0, (enum of_gpio_flags *)(&gc032a->sensor_cb)); if (!gpio_is_valid(gc032a->sensor_cb.gpio)) pr_err("%s: sensor_cb is invalid.\n", __func__); //to strip buffer or not ret = of_property_read_u32(np, "sensor_strip_buffer", &gc032a->strip_buffer); if (ret) { gc032a->strip_buffer = 0; pr_err("fetch strip_buffer config from device_tree failed\n"); } //frame head and tail size ret = of_property_read_u32(np, "sensor_frame_unit_size", &gc032a->frame_unit_size); if (ret) { gc032a->frame_unit_size = 0; pr_err("fetch frame unit size from device_tree failed\n"); } //line head and tail size ret = of_property_read_u32(np, "sensor_line_unit_size", &gc032a->line_unit_size); if (ret) { gc032a->line_unit_size = 0; pr_err("fetch line unit size from device_tree failed\n"); } /* request gpio */ gc032a->sensor_pwdn_io = os_gpio_request(&gc032a->sensor_pwdn); os_gpio_set(&gc032a->sensor_pwdn); gc032a->sensor_cs_io = os_gpio_request(&gc032a->sensor_cs); os_gpio_set(&gc032a->sensor_cs); gc032a->sensor_cb_io = os_gpio_request(&gc032a->sensor_cb); gpio_direction_input(gc032a->sensor_cb.gpio); gc032a->cb_irq = gpio_to_irq(gc032a->sensor_cb.gpio); /* request irq */ if (devm_request_irq(&spi->dev, gc032a->cb_irq, spi_sensor_isr, IRQF_TRIGGER_FALLING, "spi-sensor-cb", gc032a)) { pr_err("%s request irq %d failure\n", __func__, gc032a->cb_irq); } else { pr_info("%s request irq success, cb_irq:%d\n", __func__, gc032a->cb_irq); } disable_irq(gc032a->cb_irq); spin_lock_init(&gc032a->slock); mutex_init(&gc032a->buf_lock); INIT_LIST_HEAD(&gc032a->vidq.active); /* receive work task init */ INIT_WORK(&gc032a->spi_rx_work, spi_rx_work_handle); /* v4l2 device register */ v4l2_dev = &gc032a->v4l2_dev; strlcpy(v4l2_dev->name, "spi-sensor", sizeof(v4l2_dev->name)); ret = v4l2_device_register(&spi->dev, v4l2_dev); if (ret) pr_err("%s: error registering v4l2 device\n", __func__); dev_set_drvdata(&spi->dev, gc032a); i2c_adap = i2c_get_adapter(gc032a->twi_id); gc032a->sensor_i2c_board.addr = (unsigned short)(gc032a->twi_addr >> 1); strncpy(gc032a->sensor_i2c_board.type, spi->modalias, sizeof(gc032a->sensor_i2c_board.type)); pr_info("%s: sub device register %s i2c_addr = 0x%x start\n", __func__, gc032a->sensor_i2c_board.type, gc032a->twi_addr); gc032a->sd = v4l2_i2c_new_subdev_board(&gc032a->v4l2_dev, i2c_adap, &gc032a->sensor_i2c_board, NULL); if (IS_ERR_OR_NULL(gc032a->sd)) { i2c_put_adapter(i2c_adap); pr_err("%s: error registering v4l2 subdevice No such device!\n", __func__); } /* subdev register is OK, check sensor init */ ret = v4l2_subdev_call(gc032a->sd, core, s_power, PWR_ON); pr_info("%s: sensor power_on______________________\n", __func__); ret = v4l2_subdev_call(gc032a->sd, core, init, 0); v4l2_subdev_call(gc032a->sd, core, s_power, PWR_OFF); pr_info("%s: sensor power_off______________________\n", __func__); q = &gc032a->vb_vidq; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; q->drv_priv = gc032a; q->buf_struct_size = sizeof(struct spi_buffer); q->ops = &spi_video_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &gc032a->buf_lock; ret = vb2_queue_init(q); if (ret) { pr_err("vb2_queue_init() failed\n"); return ret; } //video_device, put vb2_queue in vdev = &gc032a->video; vdev->v4l2_dev = &gc032a->v4l2_dev; dev_set_name(&vdev->dev, "spi-sensor"); vdev->fops = &spi_sensor_fops; vdev->ioctl_ops = &spi_sensor_ioctl_ops; vdev->release = video_device_release; vdev->queue = &gc032a->vb_vidq; vdev->lock = &gc032a->buf_lock; ret = video_register_device(vdev, VFL_TYPE_GRABBER, 0); if (ret < 0) pr_err("%s: video register device fail\n", __func__); video_set_drvdata(vdev, gc032a); mutex_init(&gc032a->spi_rx_mutex); spi->bits_per_word = 8; spi_setup(spi); gc032a->spidev = spi; gc032a->state = 0; return ret; } static int spi_sensor_remove(struct spi_device *spi) { struct spi_sensor *gc032a = (struct spi_sensor *)dev_get_drvdata(&spi->dev); os_gpio_release(gc032a->sensor_pwdn_io); os_gpio_release(gc032a->sensor_cs_io); os_gpio_release(gc032a->sensor_cb_io); devm_free_irq(&spi->dev, gc032a->cb_irq, (void *)gc032a); video_unregister_device(&gc032a->video); v4l2_device_unregister(&gc032a->v4l2_dev); //spin_destroy(gc032a->slock); mutex_destroy(&gc032a->spi_rx_mutex); mutex_destroy(&gc032a->buf_lock); dev_set_drvdata(&spi->dev, NULL); kfree(gc032a); return 0; } static const struct spi_device_id spi_sensor_ids[] = { {SPI_SENSOR_NAME, 0} }; static const struct of_device_id of_match_spi_sensor[] = { {.compatible = SPI_SENSOR_NAME,}, }; static struct spi_driver spi_sensor_driver = { .probe = spi_sensor_probe, .remove = spi_sensor_remove, .driver = { .name = SPI_SENSOR_NAME, .owner = THIS_MODULE, .of_match_table = of_match_spi_sensor, }, .id_table = spi_sensor_ids, }; static int spi_sensor_init(void) { pr_info("%s\n", __func__); spi_register_driver(&spi_sensor_driver); return 0; } static void spi_sensor_exit(void) { pr_info("%s\n", __func__); spi_unregister_driver(&spi_sensor_driver); } module_init(spi_sensor_init); module_exit(spi_sensor_exit); MODULE_LICENSE("GPL");