162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * gspca ViCam subdriver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011 Hans de Goede <hdegoede@redhat.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on the usbvideo vicam driver, which is: 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (c) 2002 Joe Burks (jburks@wavicle.org), 1062306a36Sopenharmony_ci * Chris Cheney (chris.cheney@gmail.com), 1162306a36Sopenharmony_ci * Pavel Machek (pavel@ucw.cz), 1262306a36Sopenharmony_ci * John Tyner (jtyner@cs.ucr.edu), 1362306a36Sopenharmony_ci * Monroe Williams (monroe@pobox.com) 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define MODULE_NAME "vicam" 1962306a36Sopenharmony_ci#define HEADER_SIZE 64 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/workqueue.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/firmware.h> 2462306a36Sopenharmony_ci#include <linux/ihex.h> 2562306a36Sopenharmony_ci#include "gspca.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define VICAM_FIRMWARE "vicam/firmware.fw" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 3062306a36Sopenharmony_ciMODULE_DESCRIPTION("GSPCA ViCam USB Camera Driver"); 3162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3262306a36Sopenharmony_ciMODULE_FIRMWARE(VICAM_FIRMWARE); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct sd { 3562306a36Sopenharmony_ci struct gspca_dev gspca_dev; /* !! must be the first item */ 3662306a36Sopenharmony_ci struct work_struct work_struct; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* The vicam sensor has a resolution of 512 x 244, with I believe square 4062306a36Sopenharmony_ci pixels, but this is forced to a 4:3 ratio by optics. So it has 4162306a36Sopenharmony_ci non square pixels :( */ 4262306a36Sopenharmony_cistatic struct v4l2_pix_format vicam_mode[] = { 4362306a36Sopenharmony_ci { 256, 122, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, 4462306a36Sopenharmony_ci .bytesperline = 256, 4562306a36Sopenharmony_ci .sizeimage = 256 * 122, 4662306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB,}, 4762306a36Sopenharmony_ci /* 2 modes with somewhat more square pixels */ 4862306a36Sopenharmony_ci { 256, 200, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, 4962306a36Sopenharmony_ci .bytesperline = 256, 5062306a36Sopenharmony_ci .sizeimage = 256 * 200, 5162306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB,}, 5262306a36Sopenharmony_ci { 256, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, 5362306a36Sopenharmony_ci .bytesperline = 256, 5462306a36Sopenharmony_ci .sizeimage = 256 * 240, 5562306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB,}, 5662306a36Sopenharmony_ci#if 0 /* This mode has extremely non square pixels, testing use only */ 5762306a36Sopenharmony_ci { 512, 122, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, 5862306a36Sopenharmony_ci .bytesperline = 512, 5962306a36Sopenharmony_ci .sizeimage = 512 * 122, 6062306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB,}, 6162306a36Sopenharmony_ci#endif 6262306a36Sopenharmony_ci { 512, 244, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, 6362306a36Sopenharmony_ci .bytesperline = 512, 6462306a36Sopenharmony_ci .sizeimage = 512 * 244, 6562306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB,}, 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int vicam_control_msg(struct gspca_dev *gspca_dev, u8 request, 6962306a36Sopenharmony_ci u16 value, u16 index, u8 *data, u16 len) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci int ret; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci ret = usb_control_msg(gspca_dev->dev, 7462306a36Sopenharmony_ci usb_sndctrlpipe(gspca_dev->dev, 0), 7562306a36Sopenharmony_ci request, 7662306a36Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 7762306a36Sopenharmony_ci value, index, data, len, 1000); 7862306a36Sopenharmony_ci if (ret < 0) 7962306a36Sopenharmony_ci pr_err("control msg req %02X error %d\n", request, ret); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return ret; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int vicam_set_camera_power(struct gspca_dev *gspca_dev, int state) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci int ret; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci ret = vicam_control_msg(gspca_dev, 0x50, state, 0, NULL, 0); 8962306a36Sopenharmony_ci if (ret < 0) 9062306a36Sopenharmony_ci return ret; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (state) 9362306a36Sopenharmony_ci ret = vicam_control_msg(gspca_dev, 0x55, 1, 0, NULL, 0); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return ret; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* 9962306a36Sopenharmony_ci * request and read a block of data 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_cistatic int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci int ret, unscaled_height, act_len = 0; 10462306a36Sopenharmony_ci u8 *req_data = gspca_dev->usb_buf; 10562306a36Sopenharmony_ci s32 expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure); 10662306a36Sopenharmony_ci s32 gain = v4l2_ctrl_g_ctrl(gspca_dev->gain); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci memset(req_data, 0, 16); 10962306a36Sopenharmony_ci req_data[0] = gain; 11062306a36Sopenharmony_ci if (gspca_dev->pixfmt.width == 256) 11162306a36Sopenharmony_ci req_data[1] |= 0x01; /* low nibble x-scale */ 11262306a36Sopenharmony_ci if (gspca_dev->pixfmt.height <= 122) { 11362306a36Sopenharmony_ci req_data[1] |= 0x10; /* high nibble y-scale */ 11462306a36Sopenharmony_ci unscaled_height = gspca_dev->pixfmt.height * 2; 11562306a36Sopenharmony_ci } else 11662306a36Sopenharmony_ci unscaled_height = gspca_dev->pixfmt.height; 11762306a36Sopenharmony_ci req_data[2] = 0x90; /* unknown, does not seem to do anything */ 11862306a36Sopenharmony_ci if (unscaled_height <= 200) 11962306a36Sopenharmony_ci req_data[3] = 0x06; /* vend? */ 12062306a36Sopenharmony_ci else if (unscaled_height <= 242) /* Yes 242 not 240 */ 12162306a36Sopenharmony_ci req_data[3] = 0x07; /* vend? */ 12262306a36Sopenharmony_ci else /* Up to 244 lines with req_data[3] == 0x08 */ 12362306a36Sopenharmony_ci req_data[3] = 0x08; /* vend? */ 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (expo < 256) { 12662306a36Sopenharmony_ci /* Frame rate maxed out, use partial frame expo time */ 12762306a36Sopenharmony_ci req_data[4] = 255 - expo; 12862306a36Sopenharmony_ci req_data[5] = 0x00; 12962306a36Sopenharmony_ci req_data[6] = 0x00; 13062306a36Sopenharmony_ci req_data[7] = 0x01; 13162306a36Sopenharmony_ci } else { 13262306a36Sopenharmony_ci /* Modify frame rate */ 13362306a36Sopenharmony_ci req_data[4] = 0x00; 13462306a36Sopenharmony_ci req_data[5] = 0x00; 13562306a36Sopenharmony_ci req_data[6] = expo & 0xFF; 13662306a36Sopenharmony_ci req_data[7] = expo >> 8; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci req_data[8] = ((244 - unscaled_height) / 2) & ~0x01; /* vstart */ 13962306a36Sopenharmony_ci /* bytes 9-15 do not seem to affect exposure or image quality */ 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci mutex_lock(&gspca_dev->usb_lock); 14262306a36Sopenharmony_ci ret = vicam_control_msg(gspca_dev, 0x51, 0x80, 0, req_data, 16); 14362306a36Sopenharmony_ci mutex_unlock(&gspca_dev->usb_lock); 14462306a36Sopenharmony_ci if (ret < 0) 14562306a36Sopenharmony_ci return ret; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci ret = usb_bulk_msg(gspca_dev->dev, 14862306a36Sopenharmony_ci usb_rcvbulkpipe(gspca_dev->dev, 0x81), 14962306a36Sopenharmony_ci data, size, &act_len, 10000); 15062306a36Sopenharmony_ci /* successful, it returns 0, otherwise negative */ 15162306a36Sopenharmony_ci if (ret < 0 || act_len != size) { 15262306a36Sopenharmony_ci pr_err("bulk read fail (%d) len %d/%d\n", 15362306a36Sopenharmony_ci ret, act_len, size); 15462306a36Sopenharmony_ci return -EIO; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* 16062306a36Sopenharmony_ci * This function is called as a workqueue function and runs whenever the camera 16162306a36Sopenharmony_ci * is streaming data. Because it is a workqueue function it is allowed to sleep 16262306a36Sopenharmony_ci * so we can use synchronous USB calls. To avoid possible collisions with other 16362306a36Sopenharmony_ci * threads attempting to use gspca_dev->usb_buf we take the usb_lock when 16462306a36Sopenharmony_ci * performing USB operations using it. In practice we don't really need this 16562306a36Sopenharmony_ci * as the cameras controls are only written from the workqueue. 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_cistatic void vicam_dostream(struct work_struct *work) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct sd *sd = container_of(work, struct sd, work_struct); 17062306a36Sopenharmony_ci struct gspca_dev *gspca_dev = &sd->gspca_dev; 17162306a36Sopenharmony_ci int ret, frame_sz; 17262306a36Sopenharmony_ci u8 *buffer; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage + 17562306a36Sopenharmony_ci HEADER_SIZE; 17662306a36Sopenharmony_ci buffer = kmalloc(frame_sz, GFP_KERNEL); 17762306a36Sopenharmony_ci if (!buffer) { 17862306a36Sopenharmony_ci pr_err("Couldn't allocate USB buffer\n"); 17962306a36Sopenharmony_ci goto exit; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci while (gspca_dev->present && gspca_dev->streaming) { 18362306a36Sopenharmony_ci#ifdef CONFIG_PM 18462306a36Sopenharmony_ci if (gspca_dev->frozen) 18562306a36Sopenharmony_ci break; 18662306a36Sopenharmony_ci#endif 18762306a36Sopenharmony_ci ret = vicam_read_frame(gspca_dev, buffer, frame_sz); 18862306a36Sopenharmony_ci if (ret < 0) 18962306a36Sopenharmony_ci break; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* Note the frame header contents seem to be completely 19262306a36Sopenharmony_ci constant, they do not change with either image, or 19362306a36Sopenharmony_ci settings. So we simply discard it. The frames have 19462306a36Sopenharmony_ci a very similar 64 byte footer, which we don't even 19562306a36Sopenharmony_ci bother reading from the cam */ 19662306a36Sopenharmony_ci gspca_frame_add(gspca_dev, FIRST_PACKET, 19762306a36Sopenharmony_ci buffer + HEADER_SIZE, 19862306a36Sopenharmony_ci frame_sz - HEADER_SIZE); 19962306a36Sopenharmony_ci gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ciexit: 20262306a36Sopenharmony_ci kfree(buffer); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci/* This function is called at probe time just before sd_init */ 20662306a36Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev, 20762306a36Sopenharmony_ci const struct usb_device_id *id) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct cam *cam = &gspca_dev->cam; 21062306a36Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* We don't use the buffer gspca allocates so make it small. */ 21362306a36Sopenharmony_ci cam->bulk = 1; 21462306a36Sopenharmony_ci cam->bulk_size = 64; 21562306a36Sopenharmony_ci cam->cam_mode = vicam_mode; 21662306a36Sopenharmony_ci cam->nmodes = ARRAY_SIZE(vicam_mode); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci INIT_WORK(&sd->work_struct, vicam_dostream); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci/* this function is called at probe and resume time */ 22462306a36Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci int ret; 22762306a36Sopenharmony_ci const struct ihex_binrec *rec; 22862306a36Sopenharmony_ci const struct firmware *fw; 22962306a36Sopenharmony_ci u8 *firmware_buf; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci ret = request_ihex_firmware(&fw, VICAM_FIRMWARE, 23262306a36Sopenharmony_ci &gspca_dev->dev->dev); 23362306a36Sopenharmony_ci if (ret) { 23462306a36Sopenharmony_ci pr_err("Failed to load \"vicam/firmware.fw\": %d\n", ret); 23562306a36Sopenharmony_ci return ret; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci firmware_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 23962306a36Sopenharmony_ci if (!firmware_buf) { 24062306a36Sopenharmony_ci ret = -ENOMEM; 24162306a36Sopenharmony_ci goto exit; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) { 24462306a36Sopenharmony_ci memcpy(firmware_buf, rec->data, be16_to_cpu(rec->len)); 24562306a36Sopenharmony_ci ret = vicam_control_msg(gspca_dev, 0xff, 0, 0, firmware_buf, 24662306a36Sopenharmony_ci be16_to_cpu(rec->len)); 24762306a36Sopenharmony_ci if (ret < 0) 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci kfree(firmware_buf); 25262306a36Sopenharmony_ciexit: 25362306a36Sopenharmony_ci release_firmware(fw); 25462306a36Sopenharmony_ci return ret; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/* Set up for getting frames. */ 25862306a36Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 26162306a36Sopenharmony_ci int ret; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci ret = vicam_set_camera_power(gspca_dev, 1); 26462306a36Sopenharmony_ci if (ret < 0) 26562306a36Sopenharmony_ci return ret; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci schedule_work(&sd->work_struct); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci/* called on streamoff with alt==0 and on disconnect */ 27362306a36Sopenharmony_ci/* the usb_lock is held at entry - restore on exit */ 27462306a36Sopenharmony_cistatic void sd_stop0(struct gspca_dev *gspca_dev) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct sd *dev = (struct sd *)gspca_dev; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* wait for the work queue to terminate */ 27962306a36Sopenharmony_ci mutex_unlock(&gspca_dev->usb_lock); 28062306a36Sopenharmony_ci /* This waits for vicam_dostream to finish */ 28162306a36Sopenharmony_ci flush_work(&dev->work_struct); 28262306a36Sopenharmony_ci mutex_lock(&gspca_dev->usb_lock); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (gspca_dev->present) 28562306a36Sopenharmony_ci vicam_set_camera_power(gspca_dev, 0); 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci gspca_dev->vdev.ctrl_handler = hdl; 29362306a36Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 2); 29462306a36Sopenharmony_ci gspca_dev->exposure = v4l2_ctrl_new_std(hdl, NULL, 29562306a36Sopenharmony_ci V4L2_CID_EXPOSURE, 0, 2047, 1, 256); 29662306a36Sopenharmony_ci gspca_dev->gain = v4l2_ctrl_new_std(hdl, NULL, 29762306a36Sopenharmony_ci V4L2_CID_GAIN, 0, 255, 1, 200); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (hdl->error) { 30062306a36Sopenharmony_ci pr_err("Could not initialize controls\n"); 30162306a36Sopenharmony_ci return hdl->error; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci return 0; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci/* Table of supported USB devices */ 30762306a36Sopenharmony_cistatic const struct usb_device_id device_table[] = { 30862306a36Sopenharmony_ci {USB_DEVICE(0x04c1, 0x009d)}, 30962306a36Sopenharmony_ci {USB_DEVICE(0x0602, 0x1001)}, 31062306a36Sopenharmony_ci {} 31162306a36Sopenharmony_ci}; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci/* sub-driver description */ 31662306a36Sopenharmony_cistatic const struct sd_desc sd_desc = { 31762306a36Sopenharmony_ci .name = MODULE_NAME, 31862306a36Sopenharmony_ci .config = sd_config, 31962306a36Sopenharmony_ci .init = sd_init, 32062306a36Sopenharmony_ci .init_controls = sd_init_controls, 32162306a36Sopenharmony_ci .start = sd_start, 32262306a36Sopenharmony_ci .stop0 = sd_stop0, 32362306a36Sopenharmony_ci}; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci/* -- device connect -- */ 32662306a36Sopenharmony_cistatic int sd_probe(struct usb_interface *intf, 32762306a36Sopenharmony_ci const struct usb_device_id *id) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci return gspca_dev_probe(intf, id, 33062306a36Sopenharmony_ci &sd_desc, 33162306a36Sopenharmony_ci sizeof(struct sd), 33262306a36Sopenharmony_ci THIS_MODULE); 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic struct usb_driver sd_driver = { 33662306a36Sopenharmony_ci .name = MODULE_NAME, 33762306a36Sopenharmony_ci .id_table = device_table, 33862306a36Sopenharmony_ci .probe = sd_probe, 33962306a36Sopenharmony_ci .disconnect = gspca_disconnect, 34062306a36Sopenharmony_ci#ifdef CONFIG_PM 34162306a36Sopenharmony_ci .suspend = gspca_suspend, 34262306a36Sopenharmony_ci .resume = gspca_resume, 34362306a36Sopenharmony_ci .reset_resume = gspca_resume, 34462306a36Sopenharmony_ci#endif 34562306a36Sopenharmony_ci}; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cimodule_usb_driver(sd_driver); 348