brintos

brintos / linux-shallow public Read only

0
0
Text · 13.6 KiB · cff64d8 Raw
545 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/* Driver for Philips webcam3   Functions that send various control messages to the webcam, including4   video modes.5   (C) 1999-2003 Nemosoft Unv.6   (C) 2004-2006 Luc Saillard (luc@saillard.org)7   (C) 2011 Hans de Goede <hdegoede@redhat.com>8 9   NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx10   driver and thus may have bugs that are not present in the original version.11   Please send bug reports and support requests to <luc@saillard.org>.12 13   NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx14   driver and thus may have bugs that are not present in the original version.15   Please send bug reports and support requests to <luc@saillard.org>.16   The decompression routines have been implemented by reverse-engineering the17   Nemosoft binary pwcx module. Caveat emptor.18 19*/20 21/*22   Changes23   2001/08/03  Alvarado   Added methods for changing white balance and24			  red/green gains25 */26 27/* Control functions for the cam; brightness, contrast, video mode, etc. */28 29#ifdef __KERNEL__30#include <linux/uaccess.h>31#endif32#include <asm/errno.h>33 34#include "pwc.h"35#include "pwc-kiara.h"36#include "pwc-timon.h"37#include "pwc-dec1.h"38#include "pwc-dec23.h"39 40/* Selectors for status controls used only in this file */41#define GET_STATUS_B00				0x0B0042#define SENSOR_TYPE_FORMATTER1			0x0C0043#define GET_STATUS_3000				0x300044#define READ_RAW_Y_MEAN_FORMATTER		0x310045#define SET_POWER_SAVE_MODE_FORMATTER		0x320046#define MIRROR_IMAGE_FORMATTER			0x330047#define LED_FORMATTER				0x340048#define LOWLIGHT				0x350049#define GET_STATUS_3600				0x360050#define SENSOR_TYPE_FORMATTER2			0x370051#define GET_STATUS_3800				0x380052#define GET_STATUS_4000				0x400053#define GET_STATUS_4100				0x4100	/* Get */54#define CTL_STATUS_4200				0x4200	/* [GS] 1 */55 56/* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */57#define VIDEO_OUTPUT_CONTROL_FORMATTER		0x010058 59static const char *size2name[PSZ_MAX] =60{61	"subQCIF",62	"QSIF",63	"QCIF",64	"SIF",65	"CIF",66	"VGA",67};68 69/********/70 71/* Entries for the Nala (645/646) camera; the Nala doesn't have compression72   preferences, so you either get compressed or non-compressed streams.73 74   An alternate value of 0 means this mode is not available at all.75 */76 77#define PWC_FPS_MAX_NALA 878 79struct Nala_table_entry {80	char alternate;			/* USB alternate setting */81	int compressed;			/* Compressed yes/no */82 83	unsigned char mode[3];		/* precomputed mode table */84};85 86static unsigned int Nala_fps_vector[PWC_FPS_MAX_NALA] = { 4, 5, 7, 10, 12, 15, 20, 24 };87 88static struct Nala_table_entry Nala_table[PSZ_MAX][PWC_FPS_MAX_NALA] =89{90#include "pwc-nala.h"91};92 93/****************************************************************************/94 95static int recv_control_msg(struct pwc_device *pdev,96	u8 request, u16 value, int recv_count)97{98	int rc;99 100	rc = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),101		request,102		USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,103		value, pdev->vcinterface,104		pdev->ctrl_buf, recv_count, USB_CTRL_GET_TIMEOUT);105	if (rc < 0)106		PWC_ERROR("recv_control_msg error %d req %02x val %04x\n",107			  rc, request, value);108	return rc;109}110 111static inline int send_video_command(struct pwc_device *pdev,112	int index, const unsigned char *buf, int buflen)113{114	int rc;115 116	memcpy(pdev->ctrl_buf, buf, buflen);117 118	rc = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),119			SET_EP_STREAM_CTL,120			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,121			VIDEO_OUTPUT_CONTROL_FORMATTER, index,122			pdev->ctrl_buf, buflen, USB_CTRL_SET_TIMEOUT);123	if (rc >= 0)124		memcpy(pdev->cmd_buf, buf, buflen);125	else126		PWC_ERROR("send_video_command error %d\n", rc);127 128	return rc;129}130 131int send_control_msg(struct pwc_device *pdev,132	u8 request, u16 value, void *buf, int buflen)133{134	return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),135			request,136			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,137			value, pdev->vcinterface,138			buf, buflen, USB_CTRL_SET_TIMEOUT);139}140 141static int set_video_mode_Nala(struct pwc_device *pdev, int size, int pixfmt,142			       int frames, int *compression, int send_to_cam)143{144	int fps, ret = 0;145	struct Nala_table_entry *pEntry;146	int frames2frames[31] =147	{ /* closest match of framerate */148	   0,  0,  0,  0,  4,  /*  0-4  */149	   5,  5,  7,  7, 10,  /*  5-9  */150	  10, 10, 12, 12, 15,  /* 10-14 */151	  15, 15, 15, 20, 20,  /* 15-19 */152	  20, 20, 20, 24, 24,  /* 20-24 */153	  24, 24, 24, 24, 24,  /* 25-29 */154	  24                   /* 30    */155	};156	int frames2table[31] =157	{ 0, 0, 0, 0, 0, /*  0-4  */158	  1, 1, 1, 2, 2, /*  5-9  */159	  3, 3, 4, 4, 4, /* 10-14 */160	  5, 5, 5, 5, 5, /* 15-19 */161	  6, 6, 6, 6, 7, /* 20-24 */162	  7, 7, 7, 7, 7, /* 25-29 */163	  7              /* 30    */164	};165 166	if (size < 0 || size > PSZ_CIF)167		return -EINVAL;168	if (frames < 4)169		frames = 4;170	else if (size > PSZ_QCIF && frames > 15)171		frames = 15;172	else if (frames > 25)173		frames = 25;174	frames = frames2frames[frames];175	fps = frames2table[frames];176	pEntry = &Nala_table[size][fps];177	if (pEntry->alternate == 0)178		return -EINVAL;179 180	if (send_to_cam)181		ret = send_video_command(pdev, pdev->vendpoint,182					 pEntry->mode, 3);183	if (ret < 0)184		return ret;185 186	if (pEntry->compressed && pixfmt == V4L2_PIX_FMT_YUV420)187		pwc_dec1_init(pdev, pEntry->mode);188 189	/* Set various parameters */190	pdev->pixfmt = pixfmt;191	pdev->vframes = frames;192	pdev->valternate = pEntry->alternate;193	pdev->width  = pwc_image_sizes[size][0];194	pdev->height = pwc_image_sizes[size][1];195	pdev->frame_size = (pdev->width * pdev->height * 3) / 2;196	if (pEntry->compressed) {197		if (pdev->release < 5) { /* 4 fold compression */198			pdev->vbandlength = 528;199			pdev->frame_size /= 4;200		}201		else {202			pdev->vbandlength = 704;203			pdev->frame_size /= 3;204		}205	}206	else207		pdev->vbandlength = 0;208 209	/* Let pwc-if.c:isoc_init know we don't support higher compression */210	*compression = 3;211 212	return 0;213}214 215 216static int set_video_mode_Timon(struct pwc_device *pdev, int size, int pixfmt,217				int frames, int *compression, int send_to_cam)218{219	const struct Timon_table_entry *pChoose;220	int fps, ret = 0;221 222	if (size >= PSZ_MAX || *compression < 0 || *compression > 3)223		return -EINVAL;224	if (frames < 5)225		frames = 5;226	else if (size == PSZ_VGA && frames > 15)227		frames = 15;228	else if (frames > 30)229		frames = 30;230	fps = (frames / 5) - 1;231 232	/* Find a supported framerate with progressively higher compression */233	do {234		pChoose = &Timon_table[size][fps][*compression];235		if (pChoose->alternate != 0)236			break;237		(*compression)++;238	} while (*compression <= 3);239 240	if (pChoose->alternate == 0)241		return -ENOENT; /* Not supported. */242 243	if (send_to_cam)244		ret = send_video_command(pdev, pdev->vendpoint,245					 pChoose->mode, 13);246	if (ret < 0)247		return ret;248 249	if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420)250		pwc_dec23_init(pdev, pChoose->mode);251 252	/* Set various parameters */253	pdev->pixfmt = pixfmt;254	pdev->vframes = (fps + 1) * 5;255	pdev->valternate = pChoose->alternate;256	pdev->width  = pwc_image_sizes[size][0];257	pdev->height = pwc_image_sizes[size][1];258	pdev->vbandlength = pChoose->bandlength;259	if (pChoose->bandlength > 0)260		pdev->frame_size = (pChoose->bandlength * pdev->height) / 4;261	else262		pdev->frame_size = (pdev->width * pdev->height * 12) / 8;263	return 0;264}265 266 267static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int pixfmt,268				int frames, int *compression, int send_to_cam)269{270	const struct Kiara_table_entry *pChoose;271	int fps, ret = 0;272 273	if (size >= PSZ_MAX || *compression < 0 || *compression > 3)274		return -EINVAL;275	if (frames < 5)276		frames = 5;277	else if (size == PSZ_VGA && frames > 15)278		frames = 15;279	else if (frames > 30)280		frames = 30;281	fps = (frames / 5) - 1;282 283	/* Find a supported framerate with progressively higher compression */284	do {285		pChoose = &Kiara_table[size][fps][*compression];286		if (pChoose->alternate != 0)287			break;288		(*compression)++;289	} while (*compression <= 3);290 291	if (pChoose->alternate == 0)292		return -ENOENT; /* Not supported. */293 294	/* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */295	if (send_to_cam)296		ret = send_video_command(pdev, 4, pChoose->mode, 12);297	if (ret < 0)298		return ret;299 300	if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420)301		pwc_dec23_init(pdev, pChoose->mode);302 303	/* All set and go */304	pdev->pixfmt = pixfmt;305	pdev->vframes = (fps + 1) * 5;306	pdev->valternate = pChoose->alternate;307	pdev->width  = pwc_image_sizes[size][0];308	pdev->height = pwc_image_sizes[size][1];309	pdev->vbandlength = pChoose->bandlength;310	if (pdev->vbandlength > 0)311		pdev->frame_size = (pdev->vbandlength * pdev->height) / 4;312	else313		pdev->frame_size = (pdev->width * pdev->height * 12) / 8;314	PWC_TRACE("frame_size=%d, vframes=%d, vsize=%d, vbandlength=%d\n",315	    pdev->frame_size, pdev->vframes, size, pdev->vbandlength);316	return 0;317}318 319int pwc_set_video_mode(struct pwc_device *pdev, int width, int height,320	int pixfmt, int frames, int *compression, int send_to_cam)321{322	int ret, size;323 324	PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, pixfmt %08x).\n",325		       width, height, frames, pixfmt);326	size = pwc_get_size(pdev, width, height);327	PWC_TRACE("decode_size = %d.\n", size);328 329	if (DEVICE_USE_CODEC1(pdev->type)) {330		ret = set_video_mode_Nala(pdev, size, pixfmt, frames,331					  compression, send_to_cam);332	} else if (DEVICE_USE_CODEC3(pdev->type)) {333		ret = set_video_mode_Kiara(pdev, size, pixfmt, frames,334					   compression, send_to_cam);335	} else {336		ret = set_video_mode_Timon(pdev, size, pixfmt, frames,337					   compression, send_to_cam);338	}339	if (ret < 0) {340		PWC_ERROR("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret);341		return ret;342	}343	pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size;344	PWC_DEBUG_SIZE("Set resolution to %dx%d\n", pdev->width, pdev->height);345	return 0;346}347 348static unsigned int pwc_get_fps_Nala(struct pwc_device *pdev, unsigned int index, unsigned int size)349{350	unsigned int i;351 352	for (i = 0; i < PWC_FPS_MAX_NALA; i++) {353		if (Nala_table[size][i].alternate) {354			if (index--==0) return Nala_fps_vector[i];355		}356	}357	return 0;358}359 360static unsigned int pwc_get_fps_Kiara(struct pwc_device *pdev, unsigned int index, unsigned int size)361{362	unsigned int i;363 364	for (i = 0; i < PWC_FPS_MAX_KIARA; i++) {365		if (Kiara_table[size][i][3].alternate) {366			if (index--==0) return Kiara_fps_vector[i];367		}368	}369	return 0;370}371 372static unsigned int pwc_get_fps_Timon(struct pwc_device *pdev, unsigned int index, unsigned int size)373{374	unsigned int i;375 376	for (i=0; i < PWC_FPS_MAX_TIMON; i++) {377		if (Timon_table[size][i][3].alternate) {378			if (index--==0) return Timon_fps_vector[i];379		}380	}381	return 0;382}383 384unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size)385{386	unsigned int ret;387 388	if (DEVICE_USE_CODEC1(pdev->type)) {389		ret = pwc_get_fps_Nala(pdev, index, size);390 391	} else if (DEVICE_USE_CODEC3(pdev->type)) {392		ret = pwc_get_fps_Kiara(pdev, index, size);393 394	} else {395		ret = pwc_get_fps_Timon(pdev, index, size);396	}397 398	return ret;399}400 401int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data)402{403	int ret;404 405	ret = recv_control_msg(pdev, request, value, 1);406	if (ret < 0)407		return ret;408 409	*data = pdev->ctrl_buf[0];410	return 0;411}412 413int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data)414{415	int ret;416 417	pdev->ctrl_buf[0] = data;418	ret = send_control_msg(pdev, request, value, pdev->ctrl_buf, 1);419	if (ret < 0)420		return ret;421 422	return 0;423}424 425int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data)426{427	int ret;428 429	ret = recv_control_msg(pdev, request, value, 1);430	if (ret < 0)431		return ret;432 433	*data = ((s8 *)pdev->ctrl_buf)[0];434	return 0;435}436 437int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data)438{439	int ret;440 441	ret = recv_control_msg(pdev, request, value, 2);442	if (ret < 0)443		return ret;444 445	*data = (pdev->ctrl_buf[1] << 8) | pdev->ctrl_buf[0];446	return 0;447}448 449int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data)450{451	int ret;452 453	pdev->ctrl_buf[0] = data & 0xff;454	pdev->ctrl_buf[1] = data >> 8;455	ret = send_control_msg(pdev, request, value, pdev->ctrl_buf, 2);456	if (ret < 0)457		return ret;458 459	return 0;460}461 462int pwc_button_ctrl(struct pwc_device *pdev, u16 value)463{464	int ret;465 466	ret = send_control_msg(pdev, SET_STATUS_CTL, value, NULL, 0);467	if (ret < 0)468		return ret;469 470	return 0;471}472 473/* POWER */474void pwc_camera_power(struct pwc_device *pdev, int power)475{476	int r;477 478	if (!pdev->power_save)479		return;480 481	if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6))482		return;	/* Not supported by Nala or Timon < release 6 */483 484	if (power)485		pdev->ctrl_buf[0] = 0x00; /* active */486	else487		pdev->ctrl_buf[0] = 0xFF; /* power save */488	r = send_control_msg(pdev, SET_STATUS_CTL,489		SET_POWER_SAVE_MODE_FORMATTER, pdev->ctrl_buf, 1);490	if (r < 0)491		PWC_ERROR("Failed to power %s camera (%d)\n",492			  power ? "on" : "off", r);493}494 495int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value)496{497	int r;498 499	if (pdev->type < 730)500		return 0;501	on_value /= 100;502	off_value /= 100;503	if (on_value < 0)504		on_value = 0;505	if (on_value > 0xff)506		on_value = 0xff;507	if (off_value < 0)508		off_value = 0;509	if (off_value > 0xff)510		off_value = 0xff;511 512	pdev->ctrl_buf[0] = on_value;513	pdev->ctrl_buf[1] = off_value;514 515	r = send_control_msg(pdev,516		SET_STATUS_CTL, LED_FORMATTER, pdev->ctrl_buf, 2);517	if (r < 0)518		PWC_ERROR("Failed to set LED on/off time (%d)\n", r);519 520	return r;521}522 523#ifdef CONFIG_USB_PWC_DEBUG524int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor)525{526	int ret, request;527 528	if (pdev->type < 675)529		request = SENSOR_TYPE_FORMATTER1;530	else if (pdev->type < 730)531		return -1; /* The Vesta series doesn't have this call */532	else533		request = SENSOR_TYPE_FORMATTER2;534 535	ret = recv_control_msg(pdev, GET_STATUS_CTL, request, 1);536	if (ret < 0)537		return ret;538	if (pdev->type < 675)539		*sensor = pdev->ctrl_buf[0] | 0x100;540	else541		*sensor = pdev->ctrl_buf[0];542	return 0;543}544#endif545