[New Driver]: stand alone pac207 v4l2 driver
by Hans de Goede
Hi All,
As explained in my introduction mail I've been working on a standalone v4l2
driver for pac207 based usb webcams. I've attached the hopefully pretty clean
result to this mail.
Note that I've also done a version which is split in a generic simple usb
webcam core and a cam/controller IC specific driver, the split version is the
one I would like to move forward with.
I'm currently posting these as .c files for easy reading and compilation /
testing, but I still hope to get a lot of feedback / a thorough review, esp of
the core <-> pac207 split version as I hope to submit that as a patch for
mainline inclusion soon.
Thanks & Regards,
Hans
obj-m += pac207.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
/***************************************************************************
* Video4Linux2 driver for Pixart PAC207BCA Camera bridge and sensor *
* *
* Copyright (C) 2008 by Hans de Goede <j.w.r.degoede(a)hhs.nl> *
* Buffer management code taken from the Video4Linux2 zc030x driver: *
* Copyright (C) 2006-2007 by Luca Risolia <luca.risolia(a)studio.unibo.it> *
* PAC207BCA code derived from the gspca Pixart PAC207BCA library: *
* Copyright (C) 2005 Thomas Kaiser thomas(a)kaiser-linux.li *
* Copyright (C) 2005 Bertrik.Sikken *
* Copyleft (C) 2005 Michel Xhaard mxhaard(a)magic.fr *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/compiler.h>
#include <linux/ioctl.h>
#include <linux/poll.h>
#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/page-flags.h>
#include <linux/byteorder/generic.h>
#include <asm/page.h>
#include <asm/uaccess.h>
#include "pac207.h"
/*****************************************************************************/
static __devinitdata struct usb_device_id pac207_id_table[] = {
{ USB_DEVICE(0x041E, 0x4028) }, /* Creative VistaPlus */
{ USB_DEVICE(0x093a, 0x2460) }, /* Qtec Wb100 */
{ USB_DEVICE(0x093a, 0x2463) }, /* Philips SPC220NC */
{ USB_DEVICE(0x093a, 0x2468) }, /* Generic PAC 207 */
{ USB_DEVICE(0x093a, 0x2470) }, /* Genius GF112 */
{ USB_DEVICE(0x093a, 0x2471) }, /* Genius GF111 */
{ USB_DEVICE(0x093a, 0x2472) }, /* Genius GF111 */
{ 0 }
};
/*****************************************************************************/
MODULE_DEVICE_TABLE(usb, pac207_id_table);
MODULE_AUTHOR("Hans de Goede <j.w.r.degoede(a)hhs.nl>");
MODULE_DESCRIPTION("Pixart PAC207BCA Camera Driver");
MODULE_LICENSE("GPL");
static unsigned short debug = PAC207_DEBUG_LEVEL;
module_param(debug, ushort, 0644);
MODULE_PARM_DESC(debug,
"\n<n> Debugging information level, from 0 to 3:"
"\n0 = none (use carefully)"
"\n1 = critical errors"
"\n2 = significant informations"
"\n3 = more verbose messages"
"\nLevel 3 is useful for testing only, when only "
"one device is used."
"\nDefault value is "__MODULE_STRING(PAC207_DEBUG_LEVEL)"."
"\n");
/*****************************************************************************/
static const char pac207_sof_marker[5] = { 0xFF, 0xFF, 0x00, 0xFF, 0x96 };
static const u8 pac207_sensor_init[][8] = {
{0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0xf0}, /* 2 */
/* {0x10, 0x24, 0x06, 0x12, 0x0c, 0x01, 0x29, 0xf0}, ** 2 increase the times exposure decrease frame rate */
{0x00, 0x64, 0x64, 0x64, 0x04, 0x10, 0xF0, 0x30}, /* a reg_10 digital gain Red Green Blue Ggain */
{0x00, 0x00, 0x00, 0x70, 0xA0, 0xF8, 0x00, 0x00}, /* 12 */
{0x00, 0x00, 0x32, 0x00, 0x96, 0x00, 0xA2, 0x02}, /* 40 */
{0x32, 0x00, 0x96, 0x00, 0xA2, 0x02, 0xAF, 0x00}, /* 42 reg_66 rate control */
};
static const u8 PacReg72[] = { 0x00, 0x00, 0x36, 0x00 }; /* 48 reg_72 Rate Control end BalSize_4a =0x36 */
static const struct v4l2_pix_format pac207_pix_fmt[2] = { {
.width = 352,
.height = 288,
.pixelformat = V4L2_PIX_FMT_SBGGR8,
.field = V4L2_FIELD_NONE,
.bytesperline = 0,
.sizeimage = 352 * 288,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 8,
}, {
.width = 176,
.height = 144,
.pixelformat = V4L2_PIX_FMT_SBGGR8,
.field = V4L2_FIELD_NONE,
.bytesperline = 0,
.sizeimage = 176 * 144,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 8,
} };
static struct pac207_decompress_table_t pac207_decompress_table[256];
/*****************************************************************************/
static u32
pac207_request_buffers(struct pac207_device* cam, u32 count)
{
const struct v4l2_pix_format* p = &pac207_pix_fmt[cam->mode];
const size_t imagesize = p->width * p->height;
void* buff = NULL;
u32 i;
if (count > PAC207_MAX_FRAMES)
count = PAC207_MAX_FRAMES;
cam->nbuffers = count;
while (cam->nbuffers) {
if ((buff = vmalloc_32_user(cam->nbuffers *
PAGE_ALIGN(imagesize))))
break;
cam->nbuffers--;
}
for (i = 0; i < cam->nbuffers; i++) {
cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize);
cam->frame[i].buf.index = i;
cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize);
cam->frame[i].buf.length = imagesize;
cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
cam->frame[i].buf.sequence = 0;
cam->frame[i].buf.field = V4L2_FIELD_NONE;
cam->frame[i].buf.memory = V4L2_MEMORY_MMAP;
cam->frame[i].buf.flags = 0;
cam->frame[i].buf.bytesused = 0;
cam->frame[i].state = F_UNUSED;
}
return cam->nbuffers;
}
static void pac207_release_buffers(struct pac207_device* cam)
{
if (cam->nbuffers) {
vfree(cam->frame[0].bufmem);
cam->nbuffers = 0;
}
}
static void pac207_empty_framequeues(struct pac207_device* cam)
{
u32 i;
INIT_LIST_HEAD(&cam->inqueue);
INIT_LIST_HEAD(&cam->outqueue);
for (i = 0; i < PAC207_MAX_FRAMES; i++) {
cam->frame[i].state = F_UNUSED;
cam->frame[i].buf.bytesused = 0;
}
}
static void pac207_queue_unusedframes(struct pac207_device* cam)
{
unsigned long lock_flags;
u32 i;
for (i = 0; i < cam->nbuffers; i++)
if (cam->frame[i].state == F_UNUSED) {
cam->frame[i].state = F_QUEUED;
spin_lock_irqsave(&cam->queue_lock, lock_flags);
list_add_tail(&cam->frame[i].frame, &cam->inqueue);
spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
}
}
/*****************************************************************************/
int pac207_write_regs(struct pac207_device* cam, u16 index,
const u8 *buffer, u16 length)
{
struct usb_device* udev = cam->usbdev;
int err = 0;
u8 *kbuffer;
kbuffer = (u8 *) kmalloc(length, GFP_KERNEL);
if (!kbuffer) {
DBG(1, "Not enough memory");
return -ENOMEM;
}
memcpy(kbuffer, buffer, length);
err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
0x00, index, kbuffer, length, PAC207_CTRL_TIMEOUT);
if (err < 0)
DBG(1, "Failed to write registers to index 0x%04X, error %d)",
index, err);
kfree(kbuffer);
return err;
}
int pac207_write_reg(struct pac207_device* cam, u16 index, u16 value)
{
struct usb_device* udev = cam->usbdev;
int err;
err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
value, index, NULL, 0, PAC207_CTRL_TIMEOUT);
if (err && err != -ENODEV)
DBG(1, "Failed to write a register (index 0x%04X, "
"value 0x%02X, error %d)",index, value, err);
return err;
}
int pac207_read_reg(struct pac207_device* cam, u16 index)
{
struct usb_device* udev = cam->usbdev;
u8* buff = cam->control_buffer;
int res;
res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
0x00, index, buff, 1, PAC207_CTRL_TIMEOUT);
if (res < 0)
DBG(1, "Failed to read a register (index 0x%04X, error %d)",
index, res);
return (res >= 0) ? (int)(*buff) : res;
}
/*****************************************************************************/
/* auto gain and exposure algorithm based on the knee algorithm described here:
http://ytse.tricolour.net/docs/LowLightOptimization.html */
static void pac207_do_auto_gain(struct pac207_device* cam)
{
int i, steps, desired_avg_lum;
int orig_gain = cam->gain;
int orig_exposure = cam->exposure;
int avg_lum = atomic_read(&cam->avg_lum);
if (!cam->autogain || avg_lum == -1)
return;
if (cam->autogain_ignore_frames > 0) {
cam->autogain_ignore_frames--;
return;
}
/* correct desired lumination for the configured brightness */
desired_avg_lum = 100 + cam->brightness / 2;
/* If we are of a multiple of deadzone, do multiple step to reach the
desired lumination fast (with the risc of a slight overshoot */
steps = abs(desired_avg_lum - avg_lum) / PAC207_AUTOGAIN_DEADZONE;
for (i = 0; i < steps; i++) {
if (avg_lum > desired_avg_lum) {
if (cam->gain > PAC207_GAIN_KNEE) {
cam->gain--;
} else if (cam->exposure > PAC207_EXPOSURE_KNEE) {
cam->exposure--;
} else if (cam->gain > PAC207_GAIN_DEFAULT) {
cam->gain--;
} else if (cam->exposure > PAC207_EXPOSURE_MIN) {
cam->exposure--;
} else if (cam->gain > PAC207_GAIN_MIN) {
cam->gain--;
} else
break;
} else {
if (cam->gain < PAC207_GAIN_DEFAULT) {
cam->gain++;
} else if (cam->exposure < PAC207_EXPOSURE_KNEE) {
cam->exposure++;
} else if (cam->gain < PAC207_GAIN_KNEE) {
cam->gain++;
} else if (cam->exposure < PAC207_EXPOSURE_MAX) {
cam->exposure++;
} else if (cam->gain < PAC207_GAIN_MAX) {
cam->gain++;
} else
break;
}
}
if (cam->exposure != orig_exposure || cam->gain != orig_gain) {
if (cam->exposure != orig_exposure)
pac207_write_reg(cam, 0x0002, cam->exposure);
else
pac207_write_reg(cam, 0x000e, cam->gain);
pac207_write_reg(cam, 0x13, 0x01); /* load registers to sen. */
pac207_write_reg(cam, 0x1c, 0x01); /* not documented */
cam->autogain_ignore_frames = PAC207_AUTOGAIN_IGNORE_FRAMES;
}
}
/*****************************************************************************/
static void *pac207_find_sof(struct pac207_device* cam, void* mem,
unsigned int len)
{
char *m = mem;
unsigned int i;
/* Search for the SOF marker (fixed part) in the header */
for (i = 0; i < len; i++) {
if (m[i] == pac207_sof_marker[cam->sof_read]) {
cam->sof_read++;
if (cam->sof_read == sizeof(pac207_sof_marker)) {
DBG(3, "SOF found, bytes to analyze: %u. Frame"
"starts at byte #%u", len, i + 1);
cam->sof_read = 0;
return m + i + 1;
}
} else
cam->sof_read = 0;
}
return NULL;
}
#define CLIP(color) (unsigned char)(((color)>0xFF)?0xff:(((color)<0)?0:(color)))
/* Note len is deliberately signed here, where it is unsigned everywhere else
as it gets compared with the signed bitpos and can even become negative! */
static int pac207_decompress_row(struct pac207_device* cam,
struct pac207_frame_t* f, u8 *cdata, int len)
{
const struct v4l2_pix_format *pix_format = &pac207_pix_fmt[cam->mode];
struct pac207_decoder_state *decoder_state = &cam->decoder_state;
u8 *outp = f->bufmem + f->buf.bytesused;
int val, bitlen, bitpos = -decoder_state->no_remaining_bits;
u8 code;
/* first two pixels are stored as raw 8-bit */
while (decoder_state->line_read < 2) {
*outp++ = *cdata++;
decoder_state->line_read++;
len--;
if (len == 0)
goto decompress_exit;
}
while (decoder_state->line_read < pix_format->width) {
if (bitpos < 0) {
code = decoder_state->remaining_bits << (8 + bitpos) |
cdata[0] >> -bitpos;
} else {
u8 *addr = cdata + bitpos / 8;
code = addr[0] << (bitpos & 7) |
addr[1] >> (8 - (bitpos & 7));
}
bitlen = decoder_state->get_abs ?
6 : pac207_decompress_table[code].len;
/* Stop decompressing if we're out of input data */
if ((bitpos + bitlen) > (len * 8))
break;
if (decoder_state->get_abs) {
*outp++ = code & 0xFC;
decoder_state->line_read++;
decoder_state->get_abs = 0;
} else {
if (pac207_decompress_table[code].is_abs)
decoder_state->get_abs = 1;
else {
/* relative to left pixel */
val = outp[-2] +
pac207_decompress_table[code].val;
*outp++ = CLIP(val);
decoder_state->line_read++;
}
}
bitpos += bitlen;
}
if (decoder_state->line_read == pix_format->width) {
/* completely decompressed line, round pos to nearest word */
len -= 2 * ((bitpos + 15) / 16);
if (len < 0) {
decoder_state->discard_byte = 1;
len = 0;
}
} else {
decoder_state->remaining_bits = cdata[bitpos/8];
decoder_state->no_remaining_bits = (8 - bitpos) & 7;
len = 0;
}
decompress_exit:
f->buf.bytesused = outp - (u8 *)f->bufmem;
return len;
}
static void pac207_decode_line_init(struct pac207_device* cam)
{
struct pac207_decoder_state *decoder_state = &cam->decoder_state;
decoder_state->line_read = 0;
decoder_state->line_state = LINE_HEADER1;
decoder_state->no_remaining_bits = 0;
decoder_state->get_abs = 0;
}
static void pac207_decode_frame_init(struct pac207_device* cam)
{
struct pac207_decoder_state *decoder_state = &cam->decoder_state;
decoder_state->header_read = 0;
decoder_state->discard_byte = 0;
pac207_decode_line_init(cam);
}
static int pac207_decode(struct pac207_device* cam, struct pac207_frame_t* f,
u8 *cdata, unsigned int len)
{
struct pac207_decoder_state *decoder_state = &cam->decoder_state;
const struct v4l2_pix_format *pix_format = &pac207_pix_fmt[cam->mode];
unsigned int needed;
/* first 11 bytes after sof marker: frame header */
if (decoder_state->header_read < 11 ) {
/* get average lumination from frame header (byte 5) */
if (decoder_state->header_read < 5 ) {
needed = 5 - decoder_state->header_read;
if (len >= needed)
atomic_set(&cam->avg_lum, cdata[needed-1]);
}
/* skip the rest of the header */
needed = 11 - decoder_state->header_read;
if (len <= needed) {
decoder_state->header_read += len;
return 0;
}
cdata += needed;
len -= needed;
decoder_state->header_read = 11;
}
while (len) {
if (decoder_state->discard_byte) {
cdata++;
len--;
decoder_state->discard_byte = 0;
continue;
}
switch (decoder_state->line_state) {
case LINE_HEADER1:
decoder_state->line_marker = cdata[0] << 8;
decoder_state->line_state = LINE_HEADER2;
needed = 1;
break;
case LINE_HEADER2:
decoder_state->line_marker |= cdata[0];
switch (decoder_state->line_marker) {
case 0x0FF0:
decoder_state->line_state = LINE_UNCOMPRESSED;
break;
case 0x1EE1:
decoder_state->line_state = LINE_COMPRESSED;
break;
default:
DBG(3, "Error unknown line-header %04X",
(int)decoder_state->line_marker);
f->state = F_ERROR;
return 0;
}
needed = 1;
break;
case LINE_UNCOMPRESSED:
needed = pix_format->width - decoder_state->line_read;
if (needed > len)
needed = len;
memcpy(f->bufmem + f->buf.bytesused, cdata, needed);
f->buf.bytesused += needed;
decoder_state->line_read += needed;
break;
case LINE_COMPRESSED:
needed = len -
pac207_decompress_row(cam, f, cdata, len);
break;
}
cdata += needed;
len -= needed;
if (decoder_state->line_read == pix_format->width) {
if (f->buf.bytesused ==
pix_format->width *
pix_format->height) {
/* eureka we've got a frame */
f->state = F_DONE;
return 1;
}
pac207_decode_line_init(cam);
}
}
return 0;
}
static void pac207_urb_complete(struct urb *urb)
{
struct pac207_device* cam = urb->context;
struct pac207_frame_t** f;
int i, ret;
switch (urb->status) {
case 0:
break;
case -ENOENT: /* usb_kill_urb() called. */
case -ECONNRESET: /* usb_unlink_urb() called. */
case -ESHUTDOWN: /* The endpoint is being disabled. */
return;
default:
goto resubmit_urb;
}
f = &cam->frame_current;
if (!(*f)) {
if (list_empty(&cam->inqueue))
goto resubmit_urb;
(*f) = list_entry(cam->inqueue.next, struct pac207_frame_t,
frame);
}
for (i = 0; i < urb->number_of_packets; i++) {
unsigned int len, status;
void *pos, *sof;
len = urb->iso_frame_desc[i].actual_length;
status = urb->iso_frame_desc[i].status;
pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
if (status) {
DBG(3, "Error in isochronous frame");
(*f)->state = F_ERROR;
continue;
}
while (len) {
sof = pac207_find_sof(cam, pos, len);
if (((*f)->state == F_QUEUED ||
(*f)->state == F_ERROR) && sof) {
start_of_frame:
(*f)->state = F_GRABBING;
(*f)->buf.bytesused = 0;
do_gettimeofday(&(*f)->buf.timestamp);
pac207_decode_frame_init(cam);
len -= sof - pos;
pos = sof;
continue; /* check for another sof in packet */
}
if ((*f)->state == F_GRABBING) {
unsigned int n;
/* If sof decode until sof */
if (sof) {
n = sof - pos;
if (n > sizeof(pac207_sof_marker))
n -= sizeof(pac207_sof_marker);
else
n = 0;
} else
n = len;
if ((ret = pac207_decode(cam, *f, pos, n))) {
(*f)->buf.sequence= ++cam->frame_count;
spin_lock(&cam->queue_lock);
list_move_tail(&(*f)->frame,
&cam->outqueue);
if (!list_empty(&cam->inqueue))
(*f) = list_entry(
cam->inqueue.next,
struct pac207_frame_t,
frame);
else
(*f) = NULL;
spin_unlock(&cam->queue_lock);
wake_up_interruptible(&cam->wait_frame);
DBG(3, "Video frame captured");
if (!(*f))
goto resubmit_urb;
}
if (sof) {
if (!ret)
DBG(3, "Incomplete frame");
goto start_of_frame;
}
len -= n;
pos += n;
}
else
break;
}
}
resubmit_urb:
urb->dev = cam->usbdev;
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret < 0)
DBG(1, "usb_submit_urb() failed, error: %d", ret);
}
static void pac207_kill_and_free_urbs(struct pac207_device* cam)
{
int i;
struct urb* urb;
struct usb_device *udev = cam->usbdev;
for (i = 0; i < PAC207_URBS; i++) {
if (!(urb = cam->urb[i]))
continue;
/* Note: usb_kill_urb() is harmless on a not submitted urb */
usb_kill_urb(urb);
if (urb->transfer_buffer) {
usb_buffer_free(udev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
}
usb_free_urb(urb);
cam->urb[i] = NULL;
}
}
static void pac207_stop_transfer(struct pac207_device* cam);
static int pac207_start_transfer(struct pac207_device* cam)
{
struct usb_device *udev = cam->usbdev;
struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
struct usb_host_interface* altsetting;
unsigned int psz;
struct urb* urb;
int i, j, alt, err = -ENOSPC;
u8 mode;
if (cam->stream == STREAM_ON)
return -EBUSY;
for (alt = intf->num_altsetting - 1; alt && err == -ENOSPC; alt--) {
altsetting = usb_altnum_to_altsetting(intf, alt);
/* Find our endpoint to determine the packetsize */
for (i = 0; i < altsetting->desc.bNumEndpoints; i++) {
if (altsetting->endpoint[i].desc.bEndpointAddress ==
0x85)
break;
}
/* Endpoint not found or not isoc in this altsetting ?? */
if (i == altsetting->desc.bNumEndpoints ||
(altsetting->endpoint[i].desc.bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK) !=
USB_ENDPOINT_XFER_ISOC)
continue;
psz = le16_to_cpu(altsetting->endpoint[i].desc.wMaxPacketSize);
/* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */
psz = (psz & 0x07ff) * (1 + ((psz >> 11) & 3));
err = usb_set_interface(udev, 0, alt);
if (err) {
DBG(1, "usb_set_interface() failed");
return err;
}
for (i = 0; i < PAC207_URBS; i++) {
urb = usb_alloc_urb(PAC207_ISO_PACKETS, GFP_KERNEL);
if (!urb) {
DBG(1, "usb_alloc_urb() failed");
pac207_kill_and_free_urbs(cam);
return -ENOMEM;
}
cam->urb[i] = urb;
/* We alloc psz + 1 bytes per packet, because the
decompression code reads bytes at bit offsets and
thus can peek up to 7 bits ahead (which will not be
used if outside of the actual data, but they will
be read)! */
urb->transfer_buffer = usb_buffer_alloc(udev,
(psz + 1) *
PAC207_ISO_PACKETS,
GFP_KERNEL,
&urb->transfer_dma);
if (!urb->transfer_buffer) {
DBG(1, "usb_buffer_alloc() failed");
pac207_kill_and_free_urbs(cam);
return -ENOMEM;
}
urb->dev = udev;
urb->context = cam;
urb->pipe = usb_rcvisocpipe(udev, 0x85);
urb->transfer_flags = URB_ISO_ASAP |
URB_NO_TRANSFER_DMA_MAP;
urb->number_of_packets = PAC207_ISO_PACKETS;
urb->complete = pac207_urb_complete;
urb->transfer_buffer_length = (psz + 1) *
PAC207_ISO_PACKETS;
urb->interval = 1;
for (j = 0; j < PAC207_ISO_PACKETS; j++) {
urb->iso_frame_desc[j].offset = (psz + 1) * j;
urb->iso_frame_desc[j].length = psz;
}
}
/* gspca start methode code here */
pac207_write_reg(cam, 0x0f, 0x10); /* Power control (Bit 6-0) */
pac207_write_regs(cam, 0x0002, pac207_sensor_init[0], 8);
pac207_write_regs(cam, 0x000a, pac207_sensor_init[1], 8);
pac207_write_regs(cam, 0x0012, pac207_sensor_init[2], 8);
pac207_write_regs(cam, 0x0040, pac207_sensor_init[3], 8);
pac207_write_regs(cam, 0x0042, pac207_sensor_init[4], 8);
pac207_write_regs(cam, 0x0048, PacReg72, 4);
pac207_write_reg(cam, 0x4a, 0x88); /* Compression Balance size */
pac207_write_reg(cam, 0x4b, 0x00); /* Sram test value */
pac207_write_reg(cam, 0x08, cam->brightness);
pac207_write_reg(cam, 0x0e, cam->gain); /* PGA global gain (Bit 4-0) */
pac207_write_reg(cam, 0x02, cam->exposure); /* PXCK = 12MHz /n */
mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */
if (cam->mode) { /* 176x144 */
mode |= 0x01;
DBG(3, "pac207_start mode 176x144");
} else /* 352x288 */
DBG(3, "pac207_start mode 352x288");
pac207_write_reg(cam, 0x41, mode);
pac207_write_reg(cam, 0x13, 0x01); /* load registers to sensor (Bit 0, auto clear) */
pac207_write_reg(cam, 0x1c, 0x01); /* not documented */
udelay(1000); /* taken from gspca */
pac207_write_reg(cam, 0x40, 0x01); /* Start ISO pipe */
cam->autogain_ignore_frames = 0;
atomic_set (&cam->avg_lum, -1);
/* end camera specific code */
cam->stream = STREAM_ON;
DBG(3, "Stream on");
for (i = 0; i < PAC207_URBS; i++) {
err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
if (err) {
DBG(1, "usb_submit_urb() failed, error %d",
err);
pac207_stop_transfer(cam);
break;
}
}
}
return err;
}
static void pac207_stop_transfer(struct pac207_device* cam)
{
struct usb_device *udev = cam->usbdev;
s8 i;
int err = 0;
if (cam->stream == STREAM_OFF)
return;
/* gspca stopN methode code here */
pac207_write_reg(cam, 0x40, 0x00); /* Stop ISO pipe */
pac207_write_reg(cam, 0x41, 0x00); /* Turn of LED */
pac207_write_reg(cam, 0x0f, 0x00); /* Power Control */
/* end camera specific code */
pac207_kill_and_free_urbs(cam);
err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */
if (err && err != -ENODEV)
DBG(1, "stop_transfer: usb_set_interface() failed, error: %d",
err);
/* gspca stop0 methode code here */
cam->frame_current = NULL;
cam->stream = STREAM_OFF;
DBG(3, "Stream off");
}
/*****************************************************************************/
static int pac207_init(struct pac207_device* cam)
{
/* gspca configure method code here*/
pac207_write_reg(cam, 0x41, 0x00); /* 00 Bit_0=Image Format, Bit_1=LED, Bit_2=Compression test mode enable */
pac207_write_reg(cam, 0x0f, 0x00); /* Power Control */
pac207_write_reg(cam, 0x11, 0x30); /* Analog Bias */
return 0;
}
/*****************************************************************************/
static void pac207_release_resources(struct kref *kref)
{
struct pac207_device *cam = container_of(kref, struct pac207_device,
kref);
DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor);
video_set_drvdata(cam->v4ldev, NULL);
video_unregister_device(cam->v4ldev);
usb_put_dev(cam->usbdev);
kfree(cam->control_buffer);
kfree(cam);
}
static int pac207_open(struct inode* inode, struct file* filp)
{
struct pac207_device* cam;
int err = 0;
/* usb driver disconnect may be running */
if (!mutex_trylock(&pac207_dev_lock))
return -EAGAIN;
cam = video_get_drvdata(video_devdata(filp));
kref_get(&cam->kref);
if (cam->users) {
err = -EBUSY;
goto out;
}
/* gspca init methode code here */
filp->private_data = cam;
cam->users++;
cam->io = IO_NONE;
cam->nreadbuffers = PAC207_DEFAULT_READBUFFERS;
cam->frame_count = 0;
cam->brightness = PAC207_BRIGHTNESS_DEFAULT;
cam->exposure = PAC207_EXPOSURE_DEFAULT;
cam->gain = PAC207_GAIN_DEFAULT;
cam->autogain = 1;
/* init the frame queues */
pac207_empty_framequeues(cam);
DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor);
out:
if (err)
kref_put(&cam->kref, pac207_release_resources);
mutex_unlock(&pac207_dev_lock);
return err;
}
static int pac207_release(struct inode* inode, struct file* filp)
{
struct pac207_device* cam;
mutex_lock(&pac207_dev_lock);
cam = video_get_drvdata(video_devdata(filp));
pac207_stop_transfer(cam);
pac207_release_buffers(cam);
cam->users--;
DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor);
kref_put(&cam->kref, pac207_release_resources);
mutex_unlock(&pac207_dev_lock);
return 0;
}
static ssize_t
pac207_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
{
struct pac207_device* cam = video_get_drvdata(video_devdata(filp));
struct pac207_frame_t* f, * i;
unsigned long lock_flags;
long timeout;
int err = 0;
if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if (cam->io == IO_MMAP) {
DBG(2, "Close and open the device again to choose the read "
"method");
err = -EBUSY;
goto out;
}
if (cam->io == IO_NONE) {
if (!pac207_request_buffers(cam, cam->nreadbuffers)) {
DBG(1, "read() failed, not enough memory");
err = -ENOMEM;
goto out;
}
pac207_queue_unusedframes(cam);
if ((err = pac207_start_transfer(cam)))
goto out;
cam->io = IO_READ;
}
/* If the inqueue is depleted, chances are there are some rather old
frames in the outqueue (for example an application doing one read
every minute), so flush it. */
if (list_empty(&cam->inqueue)) {
spin_lock_irqsave(&cam->queue_lock, lock_flags);
list_for_each_entry(f, &cam->outqueue, frame)
f->state = F_UNUSED;
INIT_LIST_HEAD(&cam->outqueue);
spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
pac207_queue_unusedframes(cam);
}
if (!count)
goto out;
if (list_empty(&cam->outqueue)) {
if (filp->f_flags & O_NONBLOCK) {
err = -EAGAIN;
goto out;
}
timeout = wait_event_interruptible_timeout(
cam->wait_frame, !list_empty(&cam->outqueue),
msecs_to_jiffies(PAC207_FRAME_TIMEOUT) );
if (timeout <= 0) {
err = (timeout < 0)? timeout : -EIO;
goto out;
}
}
pac207_do_auto_gain(cam);
spin_lock_irqsave(&cam->queue_lock, lock_flags);
f = list_entry(cam->outqueue.next, struct pac207_frame_t, frame);
list_del(cam->outqueue.next);
spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
if (count > f->buf.bytesused)
count = f->buf.bytesused;
if (copy_to_user(buf, f->bufmem, count))
err = -EFAULT;
else
*f_pos += count;
f->state = F_UNUSED;
pac207_queue_unusedframes(cam);
out:
mutex_unlock(&cam->fileop_mutex);
return err ? err : count;
}
static unsigned int pac207_poll(struct file *filp, poll_table *wait)
{
struct pac207_device* cam = video_get_drvdata(video_devdata(filp));
struct pac207_frame_t* f;
unsigned long lock_flags;
unsigned int mask = 0;
if (mutex_lock_interruptible(&cam->fileop_mutex))
return POLLERR;
if (cam->io == IO_NONE) {
if (!pac207_request_buffers(cam, cam->nreadbuffers)) {
DBG(1, "poll() failed, not enough memory");
mask = POLLERR;
goto out;
}
pac207_queue_unusedframes(cam);
if (pac207_start_transfer(cam)) {
DBG(1, "poll() failed, start transfer failed");
mask = POLLERR;
goto out;
}
cam->io = IO_READ;
}
if (cam->io == IO_READ) {
/* If the inqueue is depleted, read will flush the outqueue,
so do that here, otherwise read will still block. */
if (list_empty(&cam->inqueue)) {
spin_lock_irqsave(&cam->queue_lock, lock_flags);
list_for_each_entry(f, &cam->outqueue, frame)
f->state = F_UNUSED;
INIT_LIST_HEAD(&cam->outqueue);
spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
pac207_queue_unusedframes(cam);
}
}
poll_wait(filp, &cam->wait_frame, wait);
if (!list_empty(&cam->outqueue))
mask |= POLLIN | POLLRDNORM;
out:
mutex_unlock(&cam->fileop_mutex);
return mask;
}
static void pac207_vm_open(struct vm_area_struct* vma)
{
struct pac207_frame_t* f = vma->vm_private_data;
f->vma_use_count++;
}
static void pac207_vm_close(struct vm_area_struct* vma)
{
/* NOTE: buffers are not freed here */
struct pac207_frame_t* f = vma->vm_private_data;
f->vma_use_count--;
}
static struct vm_operations_struct pac207_vm_ops = {
.open = pac207_vm_open,
.close = pac207_vm_close,
};
static int pac207_mmap(struct file* filp, struct vm_area_struct *vma)
{
struct pac207_device* cam = video_get_drvdata(video_devdata(filp));
unsigned long size = vma->vm_end - vma->vm_start,
start = vma->vm_start;
void *pos;
u32 i;
int err = 0;
if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if (!(vma->vm_flags & (VM_WRITE | VM_READ))) {
err = -EACCES;
goto out;
}
if (cam->io != IO_MMAP ||
size != PAGE_ALIGN(cam->frame[0].buf.length)) {
err = -EINVAL;
goto out;
}
for (i = 0; i < cam->nbuffers; i++) {
if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff)
break;
}
if (i == cam->nbuffers) {
err = -EINVAL;
goto out;
}
vma->vm_flags |= VM_IO;
vma->vm_flags |= VM_RESERVED;
pos = cam->frame[i].bufmem;
while (size > 0) { /* size is page-aligned */
if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
err = -EAGAIN;
goto out;
}
start += PAGE_SIZE;
pos += PAGE_SIZE;
size -= PAGE_SIZE;
}
vma->vm_ops = &pac207_vm_ops;
vma->vm_private_data = &cam->frame[i];
pac207_vm_open(vma);
out:
mutex_unlock(&cam->fileop_mutex);
return err;
}
static const struct file_operations pac207_fops = {
.owner = THIS_MODULE,
.open = pac207_open,
.release = pac207_release,
.ioctl = video_ioctl2,
.compat_ioctl = v4l_compat_ioctl32,
.read = pac207_read,
.poll = pac207_poll,
.mmap = pac207_mmap,
.llseek = no_llseek,
};
/*****************************************************************************/
static int
pac207_vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
{
struct pac207_device* cam = fh;
cap->version = LINUX_VERSION_CODE;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
V4L2_CAP_STREAMING;
strcpy(cap->driver, PAC207_NAME);
strlcpy(cap->card, cam->v4ldev->name, sizeof(cap->card));
if (usb_make_path(cam->usbdev, cap->bus_info, sizeof(cap->bus_info))
< 0)
strlcpy(cap->bus_info, cam->usbdev->dev.bus_id,
sizeof(cap->bus_info));
return 0;
}
static int
pac207_vidioc_enuminput(struct file *file, void *fh, struct v4l2_input *inp)
{
if (inp->index)
return -EINVAL;
memset(inp, 0, sizeof(*inp));
strcpy(inp->name, "Camera");
inp->type = V4L2_INPUT_TYPE_CAMERA;
return 0;
}
static int
pac207_vidioc_g_input(struct file *file, void *fh, unsigned int *i)
{
*i = 0;
return 0;
}
static int
pac207_vidioc_s_input(struct file *file, void *fh, unsigned int i)
{
if (i != 0)
return -EINVAL;
return 0;
}
static int
pac207_vidioc_query_ctrl(struct file *file, void *fh, struct v4l2_queryctrl *a)
{
const struct v4l2_queryctrl qctl[] = { {
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "brightness",
.minimum = PAC207_BRIGHTNESS_MIN,
.maximum = PAC207_BRIGHTNESS_MAX,
.step = 1,
.default_value = PAC207_BRIGHTNESS_DEFAULT,
.flags = 0,
}, {
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "exposure",
.minimum = PAC207_EXPOSURE_MIN,
.maximum = PAC207_EXPOSURE_MAX,
.step = 1,
.default_value = PAC207_EXPOSURE_DEFAULT,
.flags = 0,
}, {
.id = V4L2_CID_AUTOGAIN,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "autogain",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1,
.flags = 0,
}, {
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "gain",
.minimum = PAC207_GAIN_MIN,
.maximum = PAC207_GAIN_MAX,
.step = 1,
.default_value = PAC207_GAIN_DEFAULT,
.flags = 0,
} };
int i;
for (i = 0; i < ARRAY_SIZE(qctl); i++)
if (qctl[i].id == a->id) {
memcpy(a, &qctl[i], sizeof(qctl[0]));
return 0;
}
return -EINVAL;
}
static int
pac207_vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *a)
{
struct pac207_device* cam = fh;
switch (a->id) {
case V4L2_CID_BRIGHTNESS:
a->value = cam->brightness;
break;
case V4L2_CID_EXPOSURE:
a->value = cam->exposure;
break;
case V4L2_CID_AUTOGAIN:
a->value = cam->autogain;
break;
case V4L2_CID_GAIN:
a->value = cam->gain;
break;
default:
return -EINVAL;
}
return 0;
}
static int
pac207_vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
{
struct pac207_device* cam = fh;
int new_value = a->value;
int err = 0;
if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if ((err = pac207_vidioc_g_ctrl(file, fh, a)))
goto out;
if (a->value == new_value)
goto out;
/* don't allow mucking with gain / exposure when using autogain */
if (cam->autogain && (a->id == V4L2_CID_GAIN ||
a->id == V4L2_CID_EXPOSURE)) {
err = -EINVAL;
goto out;
}
switch (a->id) {
case V4L2_CID_BRIGHTNESS:
cam->brightness = new_value;
pac207_write_reg(cam, 0x0008, cam->brightness);
/* give brightness change time to take effect before
doing autogain based on the new brightness */
cam->autogain_ignore_frames =
PAC207_AUTOGAIN_IGNORE_FRAMES;
break;
case V4L2_CID_EXPOSURE:
cam->exposure = new_value;
pac207_write_reg(cam, 0x0002, cam->exposure);
break;
case V4L2_CID_AUTOGAIN:
cam->autogain = new_value;
/* when switching to autogain set defaults to make sure
we are on a valid point of the autogain gain /
exposure knee graph, and give this change time to
take effect before doing autogain. */
if (cam->autogain) {
cam->exposure = PAC207_EXPOSURE_DEFAULT;
cam->gain = PAC207_GAIN_DEFAULT;
cam->autogain_ignore_frames =
PAC207_AUTOGAIN_IGNORE_FRAMES;
pac207_write_reg(cam, 0x0002, cam->exposure);
pac207_write_reg(cam, 0x000e, cam->gain);
}
break;
case V4L2_CID_GAIN:
cam->gain = new_value;
pac207_write_reg(cam, 0x000e, cam->gain);
break;
/* no default needed already checked in pac207_vidioc_g_ctrl */
}
pac207_write_reg(cam, 0x13, 0x01); /* load registers to sensor */
pac207_write_reg(cam, 0x1c, 0x01); /* not documented */
out:
mutex_unlock(&cam->fileop_mutex);
return err;
}
static int
pac207_vidioc_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
{
if (f->index != 0)
return -EINVAL;
f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
f->flags = 0;
strcpy(f->description, "bayer rgb");
f->pixelformat = V4L2_PIX_FMT_SBGGR8;
memset(&f->reserved, 0, sizeof(f->reserved));
return 0;
}
static int
pac207_vidioc_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
{
struct pac207_device* cam = fh;
if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
memcpy(&(f->fmt.pix), &pac207_pix_fmt[cam->mode],
sizeof(pac207_pix_fmt[0]));
mutex_unlock(&cam->fileop_mutex);
return 0;
}
static int
pac207_vidioc_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
{
if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_SBGGR8)
return -EINVAL;
if (f->fmt.pix.width >= 352 && f->fmt.pix.height >= 288)
memcpy(&(f->fmt.pix), &pac207_pix_fmt[0],
sizeof(pac207_pix_fmt[0]));
else
memcpy(&(f->fmt.pix), &pac207_pix_fmt[1],
sizeof(pac207_pix_fmt[0]));
return 0;
}
static int
pac207_vidioc_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
{
struct pac207_device* cam = fh;
int err = 0;
if ((err = pac207_vidioc_try_fmt_cap(file, fh, f)))
return err;
if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if (cam->stream != STREAM_OFF) {
err = -EBUSY;
goto out;
}
if (f->fmt.pix.width == pac207_pix_fmt[0].width)
cam->mode = 0;
else
cam->mode = 1;
out:
mutex_unlock(&cam->fileop_mutex);
return err;
}
static int
pac207_vidioc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b)
{
u32 i;
struct pac207_device* cam = fh;
int err = 0;
if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
b->memory != V4L2_MEMORY_MMAP)
return -EINVAL;
if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if (cam->io == IO_READ || cam->stream == STREAM_ON) {
if (cam->io == IO_READ)
DBG(2, "Close and open the device again to choose the "
"mmap I/O method");
err = -EBUSY;
goto out;
}
for (i = 0; i < cam->nbuffers; i++)
if (cam->frame[i].vma_use_count) {
DBG(2, "VIDIOC_REQBUFS failed. "
"Previous buffers are still mapped.");
err = -EBUSY;
goto out;
}
pac207_release_buffers(cam);
if (b->count)
b->count = pac207_request_buffers(cam, b->count);
cam->io = b->count ? IO_MMAP : IO_NONE;
out:
mutex_unlock(&cam->fileop_mutex);
return err;
}
static int
pac207_vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
struct pac207_device* cam = fh;
int err = 0;
if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
b->index >= cam->nbuffers || cam->io != IO_MMAP) {
err = -EINVAL;
goto out;
}
memcpy(b, &cam->frame[b->index].buf, sizeof(*b));
if (cam->frame[b->index].vma_use_count)
b->flags |= V4L2_BUF_FLAG_MAPPED;
if (cam->frame[b->index].state == F_DONE)
b->flags |= V4L2_BUF_FLAG_DONE;
else if (cam->frame[b->index].state != F_UNUSED)
b->flags |= V4L2_BUF_FLAG_QUEUED;
out:
mutex_unlock(&cam->fileop_mutex);
return err;
}
static int
pac207_vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
struct pac207_device* cam = fh;
unsigned long lock_flags;
int err = 0;
if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
b->index >= cam->nbuffers ||
cam->io != IO_MMAP ||
cam->frame[b->index].state != F_UNUSED) {
err = -EINVAL;
goto out;
}
cam->frame[b->index].state = F_QUEUED;
spin_lock_irqsave(&cam->queue_lock, lock_flags);
list_add_tail(&cam->frame[b->index].frame, &cam->inqueue);
spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
out:
mutex_unlock(&cam->fileop_mutex);
return err;
}
static int
pac207_vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
struct pac207_device* cam = fh;
struct pac207_frame_t *f;
unsigned long lock_flags;
long timeout;
int err = 0;
if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP ||
cam->stream == STREAM_OFF) {
err = -EINVAL;
goto out;
}
if (list_empty(&cam->outqueue)) {
if (file->f_flags & O_NONBLOCK) {
err = -EAGAIN;
goto out;
}
timeout = wait_event_interruptible_timeout(cam->wait_frame,
!list_empty(&cam->outqueue),
msecs_to_jiffies(PAC207_FRAME_TIMEOUT) );
if (timeout <= 0) {
err = (timeout < 0)? timeout : -EIO;
goto out;
}
}
pac207_do_auto_gain(cam);
spin_lock_irqsave(&cam->queue_lock, lock_flags);
f = list_entry(cam->outqueue.next, struct pac207_frame_t, frame);
list_del(cam->outqueue.next);
spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
f->state = F_UNUSED;
memcpy(b, &f->buf, sizeof(*b));
if (f->vma_use_count)
b->flags |= V4L2_BUF_FLAG_MAPPED;
out:
mutex_unlock(&cam->fileop_mutex);
return err;
}
static int
pac207_vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
{
struct pac207_device* cam = fh;
int err = 0;
if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) {
err = -EINVAL;
goto out;
}
err = pac207_start_transfer(cam);
out:
mutex_unlock(&cam->fileop_mutex);
return err;
}
static int
pac207_vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
{
struct pac207_device* cam = fh;
int err = 0;
if (mutex_lock_interruptible(&cam->fileop_mutex))
return -ERESTARTSYS;
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) {
err = -EINVAL;
goto out;
}
pac207_stop_transfer(cam);
pac207_empty_framequeues(cam);
out:
mutex_unlock(&cam->fileop_mutex);
return err;
}
static int
pac207_vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
{
struct pac207_device* cam = fh;
if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
a->parm.capture.extendedmode = 0;
a->parm.capture.readbuffers = cam->nreadbuffers;
return 0;
}
static int
pac207_vidioc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
{
struct pac207_device* cam = fh;
if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
a->parm.capture.extendedmode = 0;
if (a->parm.capture.readbuffers == 0)
a->parm.capture.readbuffers = cam->nreadbuffers;
if (a->parm.capture.readbuffers > PAC207_MAX_FRAMES)
a->parm.capture.readbuffers = PAC207_MAX_FRAMES;
cam->nreadbuffers = a->parm.capture.readbuffers;
return 0;
}
/*****************************************************************************/
static int
pac207_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct pac207_device* cam;
int err = 0;
u8 idreg[] = { 0, 0 };
if (!(cam = kzalloc(sizeof(struct pac207_device), GFP_KERNEL)))
return -ENOMEM;
cam->usbdev = udev;
if (!(cam->control_buffer = kzalloc(4, GFP_KERNEL))) {
err = -ENOMEM;
goto fail;
}
idreg[0] = pac207_read_reg(cam, 0x0000);
idreg[1] = pac207_read_reg(cam, 0x0001);
idreg[0] = ((idreg[0] & 0x0F) << 4) | ((idreg[1] & 0xf0) >> 4);
idreg[1] = idreg[1] & 0x0f;
DBG(2, "Pixart Sensor ID 0x%02X Chips ID 0x%02X !!\n", idreg[0],
idreg[1]);
if (idreg[0] != 0x27) {
DBG(1, "Error invalid sensor ID!");
err = -ENODEV;
goto fail;
}
if (pac207_init(cam)) {
DBG(1, "Error cam initialization failed");
err = -ENODEV;
goto fail;
}
if (!(cam->v4ldev = video_device_alloc())) {
DBG(1, "video_device_alloc() failed");
err = -ENOMEM;
goto fail;
}
DBG(2, "Pixart PAC207BCA Image Processor and Control Chip detected "
"(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct);
strcpy(cam->v4ldev->name, "Pixart PAC207BCA USB Camera");
cam->v4ldev->owner = THIS_MODULE;
cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
cam->v4ldev->fops = &pac207_fops;
cam->v4ldev->release = video_device_release;
/* cam->v4ldev->debug = V4L2_DEBUG_IOCTL_ARG; */
cam->v4ldev->vidioc_querycap = pac207_vidioc_querycap;
cam->v4ldev->vidioc_enum_input = pac207_vidioc_enuminput;
cam->v4ldev->vidioc_g_input = pac207_vidioc_g_input;
cam->v4ldev->vidioc_s_input = pac207_vidioc_s_input;
cam->v4ldev->vidioc_queryctrl = pac207_vidioc_query_ctrl;
cam->v4ldev->vidioc_g_ctrl = pac207_vidioc_g_ctrl;
cam->v4ldev->vidioc_s_ctrl = pac207_vidioc_s_ctrl;
cam->v4ldev->vidioc_enum_fmt_cap = pac207_vidioc_enum_fmt_cap;
cam->v4ldev->vidioc_g_fmt_cap = pac207_vidioc_g_fmt_cap;
cam->v4ldev->vidioc_s_fmt_cap = pac207_vidioc_s_fmt_cap;
cam->v4ldev->vidioc_try_fmt_cap = pac207_vidioc_try_fmt_cap;
cam->v4ldev->vidioc_reqbufs = pac207_vidioc_reqbufs;
cam->v4ldev->vidioc_querybuf = pac207_vidioc_querybuf;
cam->v4ldev->vidioc_qbuf = pac207_vidioc_qbuf;
cam->v4ldev->vidioc_dqbuf = pac207_vidioc_dqbuf;
cam->v4ldev->vidioc_streamon = pac207_vidioc_streamon;
cam->v4ldev->vidioc_streamoff = pac207_vidioc_streamoff;
cam->v4ldev->vidioc_g_parm = pac207_vidioc_g_parm;
cam->v4ldev->vidioc_s_parm = pac207_vidioc_s_parm;
video_set_drvdata(cam->v4ldev, cam);
mutex_init(&cam->fileop_mutex);
spin_lock_init(&cam->queue_lock);
init_waitqueue_head(&cam->wait_frame);
kref_init(&cam->kref);
usb_get_dev(cam->usbdev);
err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, -1);
if (err) {
DBG(1, "V4L2 device registration failed");
usb_put_dev(cam->usbdev);
goto fail;
}
DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor);
usb_set_intfdata(intf, cam);
return 0;
fail:
if (cam) {
kfree(cam->control_buffer);
if (cam->v4ldev)
video_device_release(cam->v4ldev);
kfree(cam);
}
return err;
}
static void pac207_usb_disconnect(struct usb_interface* intf)
{
struct pac207_device* cam;
mutex_lock(&pac207_dev_lock);
cam = usb_get_intfdata(intf);
DBG(2, "Disconnecting %s...", cam->v4ldev->name);
/* No need to keep the urbs and their buffers allocated
(no-op when not open and streaming) */
mutex_lock(&cam->fileop_mutex);
pac207_stop_transfer(cam);
mutex_unlock(&cam->fileop_mutex);
kref_put(&cam->kref, pac207_release_resources);
mutex_unlock(&pac207_dev_lock);
}
static struct usb_driver pac207_usb_driver = {
.name = PAC207_NAME,
.id_table = pac207_id_table,
.probe = pac207_usb_probe,
.disconnect = pac207_usb_disconnect,
};
/*****************************************************************************/
static void pac207_init_decompress_table(void)
{
int i;
u8 is_abs, len;
s8 val;
for (i = 0; i < 256; i++) {
is_abs = 0;
val = 0;
len = 0;
if ((i & 0xC0) == 0) {
/* code 00 */
val = 0;
len = 2;
} else if ((i & 0xC0) == 0x40) {
/* code 01 */
val = -5;
len = 2;
} else if ((i & 0xC0) == 0x80) {
/* code 10 */
val = +5;
len = 2;
} else if ((i & 0xF0) == 0xC0) {
/* code 1100 */
val = -10;
len = 4;
} else if ((i & 0xF0) == 0xD0) {
/* code 1101 */
val = +10;
len = 4;
} else if ((i & 0xF8) == 0xE0) {
/* code 11100 */
val = -15;
len = 5;
} else if ((i & 0xF8) == 0xE8) {
/* code 11101 */
val = +15;
len = 5;
} else if ((i & 0xFC) == 0xF0) {
/* code 111100 */
val = -20;
len = 6;
} else if ((i & 0xFC) == 0xF4) {
/* code 111101 */
val = +20;
len = 6;
} else if ((i & 0xF8) == 0xF8) {
/* code 11111xxxxxx */
is_abs = 1;
val = 0;
len = 5;
}
pac207_decompress_table[i].is_abs = is_abs;
pac207_decompress_table[i].val = val;
pac207_decompress_table[i].len = len;
}
}
/*****************************************************************************/
static int __init pac207_module_init(void)
{
int err = 0;
pac207_init_decompress_table();
if ((err = usb_register(&pac207_usb_driver)))
printk(KERN_ERR PAC207_NAME
": usb_register() failed, error: %d", err);
return err;
}
static void __exit pac207_module_exit(void)
{
usb_deregister(&pac207_usb_driver);
}
module_init(pac207_module_init);
module_exit(pac207_module_exit);
/***************************************************************************
* Video4Linux2 driver for Pixart PAC207BCA Camera bridge and sensor *
* *
* Copyright (C) 2008 by Hans de Goede <j.w.r.degoede(a)hhs.nl> *
* Buffer management code taken from the Video4Linux2 zc030x driver: *
* Copyright (C) 2006-2007 by Luca Risolia <luca.risolia(a)studio.unibo.it> *
* PAC207BCA code derived from the gspca Pixart PAC207BCA library: *
* Copyright (C) 2005 Thomas Kaiser thomas(a)kaiser-linux.li *
* Copyleft (C) 2005 Michel Xhaard mxhaard(a)magic.fr *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
***************************************************************************/
#ifndef _PAC207_H_
#define _PAC207_H_
#include <linux/version.h>
#include <linux/usb.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <linux/device.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/types.h>
#include <linux/param.h>
#include <linux/mutex.h>
#include <linux/rwsem.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/kref.h>
/*****************************************************************************/
#define PAC207_DEBUG_LEVEL 2
#define PAC207_MAX_FRAMES 32
#define PAC207_URBS 4 /* 2 is problematic on some systems */
#define PAC207_ISO_PACKETS 16 /* was 7, gspca uses 16 */
#define PAC207_URB_TIMEOUT msecs_to_jiffies(2 * PAC207_ISO_PACKETS)
#define PAC207_CTRL_TIMEOUT 100
#define PAC207_FRAME_TIMEOUT 2000 /* ms */
#define PAC207_NAME "pac207"
#define PAC207_BRIGHTNESS_MIN 0
#define PAC207_BRIGHTNESS_MAX 255
#define PAC207_BRIGHTNESS_DEFAULT 4 /* power on default: 4 */
#define PAC207_EXPOSURE_MIN 4
#define PAC207_EXPOSURE_MAX 26
#define PAC207_EXPOSURE_DEFAULT 4 /* power on default: 3 ?? */
#define PAC207_EXPOSURE_KNEE 15
#define PAC207_GAIN_MIN 0
#define PAC207_GAIN_MAX 31
#define PAC207_GAIN_DEFAULT 9 /* power on default: 9 */
#define PAC207_GAIN_KNEE 20
#define PAC207_AUTOGAIN_DEADZONE 10
/* We calculating the autogain at the end of the transfer of a frame, at this
moment a frame with the old settings is being transmitted, and a frame is
being captured with the old settings. So if we adjust the autogain we must
ignore atleast the 2 next frames for the new settings to come into effect
before doing any other adjustments */
#define PAC207_AUTOGAIN_IGNORE_FRAMES 3
#define PAC207_DEFAULT_READBUFFERS 3
/*****************************************************************************/
enum pac207_frame_state {
F_UNUSED,
F_QUEUED,
F_GRABBING,
F_DONE,
F_ERROR,
};
struct pac207_frame_t {
void* bufmem;
struct v4l2_buffer buf;
enum pac207_frame_state state;
struct list_head frame;
unsigned long vma_use_count;
};
enum pac207_io_method {
IO_NONE,
IO_READ,
IO_MMAP,
};
enum pac207_stream_state {
STREAM_OFF,
STREAM_ON,
};
enum pac207_line_state {
LINE_HEADER1,
LINE_HEADER2,
LINE_UNCOMPRESSED,
LINE_COMPRESSED,
};
struct pac207_decompress_table_t {
u8 is_abs;
u8 len;
s8 val;
};
struct pac207_decoder_state {
u16 line_read;
u16 line_marker;
u8 line_state;
u8 header_read;
u8 remaining_bits;
s8 no_remaining_bits;
u8 get_abs;
u8 discard_byte;
};
static DEFINE_MUTEX(pac207_dev_lock);
struct pac207_device {
struct video_device* v4ldev;
struct usb_device* usbdev;
struct urb* urb[PAC207_URBS];
u8* control_buffer;
struct pac207_frame_t *frame_current, frame[PAC207_MAX_FRAMES];
struct list_head inqueue, outqueue;
struct kref kref;
struct mutex fileop_mutex;
spinlock_t queue_lock;
wait_queue_head_t wait_frame;
struct pac207_decoder_state decoder_state;
u32 frame_count, nbuffers, nreadbuffers;
u8 users;
u8 mode;
u8 io;
u8 stream;
u8 brightness;
u8 exposure;
u8 autogain;
u8 gain;
u8 sof_read;
u8 autogain_ignore_frames;
atomic_t avg_lum;
};
/*****************************************************************************/
#undef DBG
#define DBG(level, fmt, args...) \
do { \
if (debug >= (level)) { \
if ((level) == 1) \
dev_err(&cam->usbdev->dev, fmt "\n", ## args); \
else if ((level) == 2) \
dev_info(&cam->usbdev->dev, fmt "\n", ## args); \
else if ((level) >= 3) \
dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \
__FILE__, __FUNCTION__, __LINE__ , ## args); \
} \
} while (0)
#endif /* _PAC207_H_ */
16 years
MD Contact List in the USA
by Javier P Harden
Practicing MDs in America
788,790 in total <> 17,817 emails
Many different medical specialties
16 different sortable fields
reduced price is now: $390
### We will give you the lists below at no extra charge if you order this week ###
Listing of US Pharma Companies
Names and email addresses of 47,000 employees in high-ranking positions
Hospital Facilities in the USA
more than 23k hospital administrators in over 7k hospitals [worth over $300 alone)
Extensive List of Dentists in the US
Virtually every dentist in the USA with full contact details
American Chiropractors Contact List
100,000 Chiropractors in the USA (worth $250 alone)
email to: thomas.luiz_mdlist(a)hotmail.com
valid thru April 4
By sending an email with 965 in the subject you will have your email revoked
16 years
Re: rpms/kernel/devel kernel.spec, 1.537, 1.538 linux-2.6-firewire-git-pending.patch, 1.22, 1.23
by Thorsten Leemhuis
One comment:
On 25.03.2008 21:43, Jarod Wilson (jwilson) wrote:
> Author: jwilson
>
> Update of /cvs/pkgs/rpms/kernel/devel
> In directory cvs-int.fedora.redhat.com:/tmp/cvs-serv22927
>
> Modified Files:
> kernel.spec linux-2.6-firewire-git-pending.patch
> Log Message:
> * Tue Mar 25 2008 Jarod Wilson <jwilson(a)redhat.com>
> - Put %{_target_cpu} into kernel uname, and tack it onto assorted
> files and directories. Makes it possible to do parallel installs
> of say both i686 and x86_64 kernels of the same version on x86_64
> x86_64 hardware (#197065).
> [...]
> Index: kernel.spec
> ===================================================================
> RCS file: /cvs/pkgs/rpms/kernel/devel/kernel.spec,v
> retrieving revision 1.537
> retrieving revision 1.538
> diff -u -r1.537 -r1.538
> --- kernel.spec 25 Mar 2008 18:42:30 -0000 1.537
> +++ kernel.spec 25 Mar 2008 20:42:18 -0000 1.538
> [...]
> @@ -1243,11 +1243,11 @@
> # Pick the right config file for the kernel we're building
> if [ -n "$Flavour" ] ; then
> Config=kernel-%{version}-%{_target_cpu}-$Flavour.config
> - DevelDir=/usr/src/kernels/%{KVERREL}-$Flavour-%{_target_cpu}
> - DevelLink=/usr/src/kernels/%{KVERREL}$Flavour-%{_target_cpu}
> + DevelDir=/usr/src/kernels/%{KVERREL}-$Flavour
> + DevelLink=/usr/src/kernels/%{KVERREL}$Flavour
Just wondering: If we are working in this area why not finally chose one
of the two variants ("%{KVERREL}-$Flavour" or "%{KVERREL}$Flavour") and
stick to it everywhere as well? Or even use a "." as delimiter here as
well?
Then the duplication due to the "if [ -n "$Flavour" ]" could be removed
in this area of the spec file by using something like this everywhere:
DevelDir=/usr/src/kernels/%{KVERREL}${Flavour:+-${Flavour}}
> [...]
CU
knurd
16 years
shared /boot support. bz 197065
by Dave Jones
I took a stab at bz 197065 and arrived at the patch below.
Would appreciate some eyeballs before I commit from people
familiar with the macro goo in the specfile. (Hi Roland!)
Aparently pm-utils will need a change to cope with the changed
filename, but I think that should be the limit of the damage.
(oprofile will need to append the archname on the end of System.map-$ver
filenames, but they're user-passed anyway, and not coded anywhere afaik
Hmm. Not sure about Systemtap).
Comments?
Index: kernel.spec
===================================================================
RCS file: /cvs/pkgs/rpms/kernel/devel/kernel.spec,v
retrieving revision 1.521
diff -u -p -r1.521 kernel.spec
--- kernel.spec 21 Mar 2008 15:27:16 -0000 1.521
+++ kernel.spec 24 Mar 2008 19:20:58 -0000
@@ -1268,15 +1268,15 @@ BuildKernel() {
mkdir -p $RPM_BUILD_ROOT%{debuginfodir}/%{image_install_path}
%endif
mkdir -p $RPM_BUILD_ROOT/%{image_install_path}
- install -m 644 .config $RPM_BUILD_ROOT/boot/config-$KernelVer
- install -m 644 System.map $RPM_BUILD_ROOT/boot/System.map-$KernelVer
- touch $RPM_BUILD_ROOT/boot/initrd-$KernelVer.img
+ install -m 644 .config $RPM_BUILD_ROOT/boot/config-$KernelVer.%{_arch}
+ install -m 644 System.map $RPM_BUILD_ROOT/boot/System.map-$KernelVer.%{_arch}
+ touch $RPM_BUILD_ROOT/boot/initrd-$KernelVer.img.%{_arch}
if [ -f arch/$Arch/boot/zImage.stub ]; then
cp arch/$Arch/boot/zImage.stub $RPM_BUILD_ROOT/%{image_install_path}/zImage.stub-$KernelVer || :
fi
$CopyKernel $KernelImage \
- $RPM_BUILD_ROOT/%{image_install_path}/$InstallName-$KernelVer
- chmod 755 $RPM_BUILD_ROOT/%{image_install_path}/$InstallName-$KernelVer
+ $RPM_BUILD_ROOT/%{image_install_path}/$InstallName-$KernelVer.%{_arch}
+ chmod 755 $RPM_BUILD_ROOT/%{image_install_path}/$InstallName-$KernelVer.%{_arch}
mkdir -p $RPM_BUILD_ROOT/lib/modules/$KernelVer
make -s ARCH=$Arch INSTALL_MOD_PATH=$RPM_BUILD_ROOT modules_install KERNELRELEASE=$KernelVer
@@ -1570,7 +1570,7 @@ fi\
#
%define kernel_variant_posttrans(s:r:v:) \
%{expand:%%posttrans %{?-v*}}\
-/sbin/new-kernel-pkg --package kernel%{?-v:-%{-v*}} --rpmposttrans %{?1} %{KVERREL}%{?-v*} || exit $?\
+/sbin/new-kernel-pkg --package kernel%{?-v:-%{-v*}} --rpmposttrans %{?1} %{KVERREL}%{?-v*}.%{_arch} || exit $?\
%{nil}
#
@@ -1587,7 +1587,7 @@ if [ `uname -i` == "x86_64" -o `uname -i
[ -f /etc/sysconfig/kernel ]; then\
/bin/sed -i -e 's/^DEFAULTKERNEL=%{-s*}$/DEFAULTKERNEL=%{-r*}/' /etc/sysconfig/kernel || exit $?\
fi}\
-/sbin/new-kernel-pkg --package kernel%{?-v:-%{-v*}} --mkinitrd --depmod --install %{?1} %{KVERREL}%{?-v*} || exit $?\
+/sbin/new-kernel-pkg --package kernel%{?-v:-%{-v*}} --mkinitrd --depmod --install %{?1} %{KVERREL}%{?-v*}.%{_arch} || exit $?\
#if [ -x /sbin/weak-modules ]\
#then\
# /sbin/weak-modules --add-kernel %{KVERREL}%{?-v*} || exit $?\
@@ -1600,7 +1600,7 @@ fi}\
#
%define kernel_variant_preun() \
%{expand:%%preun %{?1}}\
-/sbin/new-kernel-pkg --rminitrd --rmmoddep --remove %{KVERREL}%{?1} || exit $?\
+/sbin/new-kernel-pkg --rminitrd --rmmoddep --remove %{KVERREL}%{?1}.%{_arch} || exit $?\
#if [ -x /sbin/weak-modules ]\
#then\
# /sbin/weak-modules --remove-kernel %{KVERREL}%{?1} || exit $?\
@@ -1668,10 +1668,9 @@ fi
%if %{1}\
%{expand:%%files %{?2}}\
%defattr(-,root,root)\
-/%{image_install_path}/%{?-k:%{-k*}}%{!?-k:vmlinuz}-%{KVERREL}%{?2}\
-/boot/System.map-%{KVERREL}%{?2}\
-#/boot/symvers-%{KVERREL}%{?2}.gz\
-/boot/config-%{KVERREL}%{?2}\
+/%{image_install_path}/%{?-k:%{-k*}}%{!?-k:vmlinuz}-%{KVERREL}%{?2}.%{_arch}\
+/boot/System.map-%{KVERREL}%{?2}.%{_arch}\
+/boot/config-%{KVERREL}%{?2}.%{_arch}\
%{?-a:%{-a*}}\
%dir /lib/modules/%{KVERREL}%{?2}\
/lib/modules/%{KVERREL}%{?2}/kernel\
--
http://www.codemonkey.org.uk
16 years
I want to ask Second Notice From Alpha Financial
by Jo anjoe
Hii.., Nice to know you..My name is Johansen Silalahi.I come from Indonesia..I have message from Alpha Financial U.K. about second Notice..The Text same with your email..I am received from Mr.Mr.Donnvan Housen.Director,Alpha Financial Consult.
London, U.K.The message same with you..According to you, is it true?that we received money from them?.The total sum of £6,000,000.00 GBP (Six Million British Pounds)..Do you trust it?By the way..What is your nationality?Thank you..God Bless..My email:anju_766hi@yahoo.com
Your Sincerely,
Johansen Silalahi,S.Hut
---------------------------------
Never miss a thing. Make Yahoo your homepage.
16 years
MD Directory in the US
by Tanner B Trevor
Certified MDs in the USA
788,409 in total <> 17,608 emails
MDs in many different specialties
16 different sortable fields
Cost just slashed - $393
{}{}{} GET THESE FR EE WITH EVERY ORDER THIS WEEK {}{}{}
List of American Pharma Companies
Personal email addresses (47,000 in total) and names for top level executives
American Hospitals
complete contact information for CEO's, CFO's, Directors and more - over 23,000 listings in total for more than 7,000 hospitals in the USA
Complete and Accurate Database for Dental Service Providers
Practically every dentist in the US is listed here
Directory of US Chiropractors
100,000 Chiropractors in the USA (worth $250 alone)
email to: stepto_mario.dr(a)hotmail.com
valid until mar 29
send and email with 4090 in the sub for rapid expulsion from our records
16 years
[PATCH] Merge the IMAC mode code with efifb and remove imacfb entirely.
by Peter Jones
This mail contains a patch which merges the IMAC mode code into the efifb
driver and removes the imacfb driver entirely. There are also a couple of
minor bug fixes. Any comments before I start bothering the upstream
maintainers with it?
---
drivers/video/Kconfig | 15 +--
drivers/video/Makefile | 1 -
drivers/video/efifb.c | 170 ++++++++++++++++++++--
drivers/video/imacfb.c | 376 ------------------------------------------------
4 files changed, 162 insertions(+), 400 deletions(-)
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 1bd5fb3..474c375 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -656,23 +656,14 @@ config FB_VESA
config FB_EFI
bool "EFI-based Framebuffer Support"
- depends on (FB = y) && X86
- select FB_CFB_FILLRECT
- select FB_CFB_COPYAREA
- select FB_CFB_IMAGEBLIT
- help
- This is the EFI frame buffer device driver. If the firmware on
- your platform is UEFI2.0, select Y to add support for
- Graphics Output Protocol for early console messages to appear.
-
-config FB_IMAC
- bool "Intel-based Macintosh Framebuffer Support"
depends on (FB = y) && X86 && EFI
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
help
- This is the frame buffer device driver for the Intel-based Macintosh
+ This is the EFI frame buffer device driver. If the firmware on
+ your platform is EFI 1.10 or UEFI 2.0, select Y to add support for
+ using the EFI framebuffer as your console.
config FB_HECUBA
tristate "Hecuba board support"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 11c0e5e..c789b01 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -118,7 +118,6 @@ obj-$(CONFIG_FB_OMAP) += omap/
# Platform or fallback drivers go here
obj-$(CONFIG_FB_UVESA) += uvesafb.o
obj-$(CONFIG_FB_VESA) += vesafb.o
-obj-$(CONFIG_FB_IMAC) += imacfb.o
obj-$(CONFIG_FB_EFI) += efifb.o
obj-$(CONFIG_FB_VGA16) += vga16fb.o
obj-$(CONFIG_FB_OF) += offb.o
diff --git a/drivers/video/efifb.c b/drivers/video/efifb.c
index bd779ae..cd962d0 100644
--- a/drivers/video/efifb.c
+++ b/drivers/video/efifb.c
@@ -12,9 +12,20 @@
#include <linux/fb.h>
#include <linux/platform_device.h>
#include <linux/screen_info.h>
+#include <linux/dmi.h>
#include <video/vga.h>
+typedef enum _MAC_TYPE {
+ M_I17,
+ M_I20,
+ M_MINI,
+ M_MACBOOK,
+ M_UNKNOWN
+} MAC_TYPE;
+
+/* --------------------------------------------------------------------- */
+
static struct fb_var_screeninfo efifb_defined __initdata = {
.activate = FB_ACTIVATE_NOW,
.height = -1,
@@ -33,6 +44,37 @@ static struct fb_fix_screeninfo efifb_fix __initdata = {
.visual = FB_VISUAL_TRUECOLOR,
};
+static int inverse;
+static int model = M_UNKNOWN;
+static int manual_height;
+static int manual_width;
+
+static int set_system(const struct dmi_system_id *id)
+{
+ printk(KERN_INFO "efifb: %s detected - set system to %ld\n",
+ id->ident, (long)id->driver_data);
+
+ model = (long)id->driver_data;
+
+ return 0;
+}
+
+static struct dmi_system_id __initdata dmi_system_table[] = {
+ { set_system, "iMac4,1", {
+ DMI_MATCH(DMI_BIOS_VENDOR,"Apple Computer, Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME,"iMac4,1") }, (void*)M_I17},
+ { set_system, "MacBookPro1,1", {
+ DMI_MATCH(DMI_BIOS_VENDOR,"Apple Computer, Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro1,1") }, (void*)M_I17},
+ { set_system, "MacBook1,1", {
+ DMI_MATCH(DMI_BIOS_VENDOR,"Apple Computer, Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME,"MacBook1,1")}, (void *)M_MACBOOK},
+ { set_system, "Macmini1,1", {
+ DMI_MATCH(DMI_BIOS_VENDOR,"Apple Computer, Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME,"Macmini1,1")}, (void *)M_MINI},
+ {},
+};
+
static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp,
struct fb_info *info)
@@ -67,6 +109,34 @@ static struct fb_ops efifb_ops = {
.fb_imageblit = cfb_imageblit,
};
+static int __init efifb_setup(char *options)
+{
+ char *this_opt;
+
+ if (!options || !*options)
+ return 0;
+
+ while ((this_opt = strsep(&options, ",")) != NULL) {
+ if (!*this_opt) continue;
+
+ if (!strcmp(this_opt, "inverse"))
+ inverse = 1;
+ else if (!strcmp(this_opt, "i17"))
+ model = M_I17;
+ else if (!strcmp(this_opt, "i20"))
+ model = M_I20;
+ else if (!strcmp(this_opt, "mini"))
+ model = M_MINI;
+ else if (!strcmp(this_opt, "macbook"))
+ model = M_MACBOOK;
+ else if (!strncmp(this_opt, "height:", 7))
+ manual_height = simple_strtoul(this_opt+7, NULL, 0);
+ else if (!strncmp(this_opt, "width:", 6))
+ manual_width = simple_strtoul(this_opt+6, NULL, 0);
+ }
+ return 0;
+}
+
static int __init efifb_probe(struct platform_device *dev)
{
struct fb_info *info;
@@ -74,6 +144,71 @@ static int __init efifb_probe(struct platform_device *dev)
unsigned int size_vmode;
unsigned int size_remap;
unsigned int size_total;
+ unsigned int width = screen_info.lfb_width;
+ unsigned int height = screen_info.lfb_height;
+ unsigned long base = 0;
+ unsigned int linelength = 0;
+ int request_succeeded = 0;
+
+ printk(KERN_INFO "efifb: probing for efifb\n");
+
+ switch (model) {
+ case M_I17:
+ width = 1440;
+ height = 900;
+ linelength = 1472 * 4;
+ base = 0x80010000;
+ break;
+ case M_I20:
+ width = 1680;
+ height = 1050;
+ linelength = 1728 * 4;
+ base = 0x80010000;
+ break;
+ case M_MINI:
+ width = 1024;
+ height = 768;
+ linelength = 2048 * 4;
+ base = 0x80000000;
+ break;
+ case M_MACBOOK:
+ width = 1280;
+ height = 800;
+ linelength = 2048 * 4;
+ base = 0x80000000;
+ break;
+ }
+
+ if (!screen_info.lfb_depth)
+ screen_info.lfb_depth = 32;
+ if (!screen_info.pages)
+ screen_info.pages = 1;
+ if (base && !screen_info.lfb_base)
+ screen_info.lfb_base = base;
+
+ if (manual_width)
+ width = manual_width;
+ if (width && !screen_info.lfb_width)
+ screen_info.lfb_width = width;
+ if (manual_height)
+ height = manual_height;
+ if (height && !screen_info.lfb_height)
+ screen_info.lfb_height = height;
+
+ /* just assume they're all unset if any are */
+ if (!screen_info.blue_size) {
+ screen_info.blue_size = 8;
+ screen_info.blue_pos = 0;
+ screen_info.green_size = 8;
+ screen_info.green_pos = 8;
+ screen_info.red_size = 8;
+ screen_info.red_pos = 16;
+ screen_info.rsvd_size = 8;
+ screen_info.rsvd_pos = 24;
+ }
+
+ if (linelength && !screen_info.lfb_linelength)
+ screen_info.lfb_linelength = linelength;
efifb_fix.smem_start = screen_info.lfb_base;
efifb_defined.bits_per_pixel = screen_info.lfb_depth;
@@ -98,21 +233,25 @@ static int __init efifb_probe(struct platform_device *dev)
* option to simply use size_total as that
* wastes plenty of kernel address space. */
size_remap = size_vmode * 2;
- if (size_remap < size_vmode)
- size_remap = size_vmode;
if (size_remap > size_total)
size_remap = size_total;
+ if (size_remap % PAGE_SIZE)
+ size_remap += PAGE_SIZE - (size_remap % PAGE_SIZE);
efifb_fix.smem_len = size_remap;
- if (!request_mem_region(efifb_fix.smem_start, size_total, "efifb"))
+ if (request_mem_region(efifb_fix.smem_start, size_remap, "efifb")) {
+ request_succeeded = 1;
+ } else {
/* We cannot make this fatal. Sometimes this comes from magic
spaces our resource handlers simply don't know about */
printk(KERN_WARNING
"efifb: cannot reserve video memory at 0x%lx\n",
efifb_fix.smem_start);
+ }
info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev);
if (!info) {
+ printk(KERN_ERR "efifb: cannot allocate framebuffer\n");
err = -ENOMEM;
goto err_release_mem;
}
@@ -125,7 +264,7 @@ static int __init efifb_probe(struct platform_device *dev)
"0x%x @ 0x%lx\n",
efifb_fix.smem_len, efifb_fix.smem_start);
err = -EIO;
- goto err_unmap;
+ goto err_release_fb;
}
printk(KERN_INFO "efifb: framebuffer at 0x%lx, mapped to 0x%p, "
@@ -178,25 +317,27 @@ static int __init efifb_probe(struct platform_device *dev)
info->fix = efifb_fix;
info->flags = FBINFO_FLAG_DEFAULT;
- if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
- err = -ENOMEM;
+ if ((err = fb_alloc_cmap(&info->cmap, 256, 0)) < 0) {
+ printk(KERN_ERR "efifb: cannot allocate colormap\n");
goto err_unmap;
}
- if (register_framebuffer(info) < 0) {
- err = -EINVAL;
+ if ((err = register_framebuffer(info)) < 0) {
+ printk(KERN_ERR "efifb: cannot register framebuffer\n");
goto err_fb_dealoc;
}
printk(KERN_INFO "fb%d: %s frame buffer device\n",
info->node, info->fix.id);
return 0;
err_fb_dealoc:
fb_dealloc_cmap(&info->cmap);
err_unmap:
iounmap(info->screen_base);
+err_release_fb:
framebuffer_release(info);
err_release_mem:
- release_mem_region(efifb_fix.smem_start, size_total);
+ if (request_succeeded)
+ release_mem_region(efifb_fix.smem_start, size_total);
return err;
}
@@ -214,10 +355,17 @@ static struct platform_device efifb_device = {
static int __init efifb_init(void)
{
int ret;
+ char *option = NULL;
if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
return -ENODEV;
-
+ dmi_check_system(dmi_system_table);
+ if (model == M_UNKNOWN && !screen_info.lfb_linelength)
+ return -ENODEV;
+
+ if (fb_get_options("efifb", &option))
+ return -ENODEV;
+ efifb_setup(option);
ret = platform_driver_register(&efifb_driver);
if (!ret) {
diff --git a/drivers/video/imacfb.c b/drivers/video/imacfb.c
index 9366ef2..e69de29 100644
--- a/drivers/video/imacfb.c
+++ b/drivers/video/imacfb.c
@@ -1,376 +0,0 @@
-/*
- * framebuffer driver for Intel Based Mac's
- *
- * (c) 2006 Edgar Hucek <gimli(a)dark-green.com>
- * Original imac driver written by Gerd Knorr <kraxel(a)goldbach.in-berlin.de>
- *
- */
-
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fb.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/screen_info.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/dmi.h>
-#include <linux/efi.h>
-
-#include <asm/io.h>
-
-#include <video/vga.h>
-
-typedef enum _MAC_TYPE {
- M_I17,
- M_I20,
- M_MINI,
- M_MACBOOK,
- M_UNKNOWN
-} MAC_TYPE;
-
-/* --------------------------------------------------------------------- */
-
-static struct fb_var_screeninfo imacfb_defined __initdata = {
- .activate = FB_ACTIVATE_NOW,
- .height = -1,
- .width = -1,
- .right_margin = 32,
- .upper_margin = 16,
- .lower_margin = 4,
- .vsync_len = 4,
- .vmode = FB_VMODE_NONINTERLACED,
-};
-
-static struct fb_fix_screeninfo imacfb_fix __initdata = {
- .id = "IMAC VGA",
- .type = FB_TYPE_PACKED_PIXELS,
- .accel = FB_ACCEL_NONE,
- .visual = FB_VISUAL_TRUECOLOR,
-};
-
-static int inverse;
-static int model = M_UNKNOWN;
-static int manual_height;
-static int manual_width;
-
-static int set_system(const struct dmi_system_id *id)
-{
- printk(KERN_INFO "imacfb: %s detected - set system to %ld\n",
- id->ident, (long)id->driver_data);
-
- model = (long)id->driver_data;
-
- return 0;
-}
-
-static struct dmi_system_id __initdata dmi_system_table[] = {
- { set_system, "iMac4,1", {
- DMI_MATCH(DMI_BIOS_VENDOR,"Apple Computer, Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME,"iMac4,1") }, (void*)M_I17},
- { set_system, "MacBookPro1,1", {
- DMI_MATCH(DMI_BIOS_VENDOR,"Apple Computer, Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro1,1") }, (void*)M_I17},
- { set_system, "MacBook1,1", {
- DMI_MATCH(DMI_BIOS_VENDOR,"Apple Computer, Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME,"MacBook1,1")}, (void *)M_MACBOOK},
- { set_system, "Macmini1,1", {
- DMI_MATCH(DMI_BIOS_VENDOR,"Apple Computer, Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME,"Macmini1,1")}, (void *)M_MINI},
- {},
-};
-
-#define DEFAULT_FB_MEM 1024*1024*16
-
-/* --------------------------------------------------------------------- */
-
-static int imacfb_setcolreg(unsigned regno, unsigned red, unsigned green,
- unsigned blue, unsigned transp,
- struct fb_info *info)
-{
- /*
- * Set a single color register. The values supplied are
- * already rounded down to the hardware's capabilities
- * (according to the entries in the `var' structure). Return
- * != 0 for invalid regno.
- */
-
- if (regno >= info->cmap.len)
- return 1;
-
- if (regno < 16) {
- red >>= 8;
- green >>= 8;
- blue >>= 8;
- ((u32 *)(info->pseudo_palette))[regno] =
- (red << info->var.red.offset) |
- (green << info->var.green.offset) |
- (blue << info->var.blue.offset);
- }
- return 0;
-}
-
-static struct fb_ops imacfb_ops = {
- .owner = THIS_MODULE,
- .fb_setcolreg = imacfb_setcolreg,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
-};
-
-static int __init imacfb_setup(char *options)
-{
- char *this_opt;
-
- if (!options || !*options)
- return 0;
-
- while ((this_opt = strsep(&options, ",")) != NULL) {
- if (!*this_opt) continue;
-
- if (!strcmp(this_opt, "inverse"))
- inverse = 1;
- else if (!strcmp(this_opt, "i17"))
- model = M_I17;
- else if (!strcmp(this_opt, "i20"))
- model = M_I20;
- else if (!strcmp(this_opt, "mini"))
- model = M_MINI;
- else if (!strcmp(this_opt, "macbook"))
- model = M_MACBOOK;
- else if (!strncmp(this_opt, "height:", 7))
- manual_height = simple_strtoul(this_opt+7, NULL, 0);
- else if (!strncmp(this_opt, "width:", 6))
- manual_width = simple_strtoul(this_opt+6, NULL, 0);
- }
- return 0;
-}
-
-static int __init imacfb_probe(struct platform_device *dev)
-{
- struct fb_info *info;
- int err;
- unsigned int size_vmode;
- unsigned int size_remap;
- unsigned int size_total;
-
- screen_info.lfb_depth = 32;
- screen_info.lfb_size = DEFAULT_FB_MEM / 0x10000;
- screen_info.pages=1;
- screen_info.blue_size = 8;
- screen_info.blue_pos = 0;
- screen_info.green_size = 8;
- screen_info.green_pos = 8;
- screen_info.red_size = 8;
- screen_info.red_pos = 16;
- screen_info.rsvd_size = 8;
- screen_info.rsvd_pos = 24;
-
- switch (model) {
- case M_I17:
- screen_info.lfb_width = 1440;
- screen_info.lfb_height = 900;
- screen_info.lfb_linelength = 1472 * 4;
- screen_info.lfb_base = 0x80010000;
- break;
- case M_I20:
- screen_info.lfb_width = 1680;
- screen_info.lfb_height = 1050;
- screen_info.lfb_linelength = 1728 * 4;
- screen_info.lfb_base = 0x80010000;
- break;
- case M_MINI:
- screen_info.lfb_width = 1024;
- screen_info.lfb_height = 768;
- screen_info.lfb_linelength = 2048 * 4;
- screen_info.lfb_base = 0x80000000;
- break;
- case M_MACBOOK:
- screen_info.lfb_width = 1280;
- screen_info.lfb_height = 800;
- screen_info.lfb_linelength = 2048 * 4;
- screen_info.lfb_base = 0x80000000;
- break;
- }
-
- /* if the user wants to manually specify height/width,
- we will override the defaults */
- /* TODO: eventually get auto-detection working */
- if (manual_height > 0)
- screen_info.lfb_height = manual_height;
- if (manual_width > 0)
- screen_info.lfb_width = manual_width;
-
- imacfb_fix.smem_start = screen_info.lfb_base;
- imacfb_defined.bits_per_pixel = screen_info.lfb_depth;
- imacfb_defined.xres = screen_info.lfb_width;
- imacfb_defined.yres = screen_info.lfb_height;
- imacfb_fix.line_length = screen_info.lfb_linelength;
-
- /* size_vmode -- that is the amount of memory needed for the
- * used video mode, i.e. the minimum amount of
- * memory we need. */
- size_vmode = imacfb_defined.yres * imacfb_fix.line_length;
-
- /* size_total -- all video memory we have. Used for
- * entries, ressource allocation and bounds
- * checking. */
- size_total = screen_info.lfb_size * 65536;
- if (size_total < size_vmode)
- size_total = size_vmode;
-
- /* size_remap -- the amount of video memory we are going to
- * use for imacfb. With modern cards it is no
- * option to simply use size_total as that
- * wastes plenty of kernel address space. */
- size_remap = size_vmode * 2;
- if (size_remap < size_vmode)
- size_remap = size_vmode;
- if (size_remap > size_total)
- size_remap = size_total;
- imacfb_fix.smem_len = size_remap;
-
- if (!request_mem_region(imacfb_fix.smem_start, size_total, "imacfb")) {
- printk(KERN_WARNING
- "imacfb: cannot reserve video memory at 0x%lx\n",
- imacfb_fix.smem_start);
- /* We cannot make this fatal. Sometimes this comes from magic
- spaces our resource handlers simply don't know about */
- }
-
- info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev);
- if (!info) {
- err = -ENOMEM;
- goto err_release_mem;
- }
- info->pseudo_palette = info->par;
- info->par = NULL;
-
- info->screen_base = ioremap(imacfb_fix.smem_start, imacfb_fix.smem_len);
- if (!info->screen_base) {
- printk(KERN_ERR "imacfb: abort, cannot ioremap video memory "
- "0x%x @ 0x%lx\n",
- imacfb_fix.smem_len, imacfb_fix.smem_start);
- err = -EIO;
- goto err_unmap;
- }
-
- printk(KERN_INFO "imacfb: framebuffer at 0x%lx, mapped to 0x%p, "
- "using %dk, total %dk\n",
- imacfb_fix.smem_start, info->screen_base,
- size_remap/1024, size_total/1024);
- printk(KERN_INFO "imacfb: mode is %dx%dx%d, linelength=%d, pages=%d\n",
- imacfb_defined.xres, imacfb_defined.yres,
- imacfb_defined.bits_per_pixel, imacfb_fix.line_length,
- screen_info.pages);
-
- imacfb_defined.xres_virtual = imacfb_defined.xres;
- imacfb_defined.yres_virtual = imacfb_fix.smem_len /
- imacfb_fix.line_length;
- printk(KERN_INFO "imacfb: scrolling: redraw\n");
- imacfb_defined.yres_virtual = imacfb_defined.yres;
-
- /* some dummy values for timing to make fbset happy */
- imacfb_defined.pixclock = 10000000 / imacfb_defined.xres *
- 1000 / imacfb_defined.yres;
- imacfb_defined.left_margin = (imacfb_defined.xres / 8) & 0xf8;
- imacfb_defined.hsync_len = (imacfb_defined.xres / 8) & 0xf8;
-
- imacfb_defined.red.offset = screen_info.red_pos;
- imacfb_defined.red.length = screen_info.red_size;
- imacfb_defined.green.offset = screen_info.green_pos;
- imacfb_defined.green.length = screen_info.green_size;
- imacfb_defined.blue.offset = screen_info.blue_pos;
- imacfb_defined.blue.length = screen_info.blue_size;
- imacfb_defined.transp.offset = screen_info.rsvd_pos;
- imacfb_defined.transp.length = screen_info.rsvd_size;
-
- printk(KERN_INFO "imacfb: %s: "
- "size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n",
- "Truecolor",
- screen_info.rsvd_size,
- screen_info.red_size,
- screen_info.green_size,
- screen_info.blue_size,
- screen_info.rsvd_pos,
- screen_info.red_pos,
- screen_info.green_pos,
- screen_info.blue_pos);
-
- imacfb_fix.ypanstep = 0;
- imacfb_fix.ywrapstep = 0;
-
- /* request failure does not faze us, as vgacon probably has this
- * region already (FIXME) */
- request_region(0x3c0, 32, "imacfb");
-
- info->fbops = &imacfb_ops;
- info->var = imacfb_defined;
- info->fix = imacfb_fix;
- info->flags = FBINFO_FLAG_DEFAULT;
-
- if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
- err = -ENOMEM;
- goto err_unmap;
- }
- if (register_framebuffer(info)<0) {
- err = -EINVAL;
- goto err_fb_dealoc;
- }
- printk(KERN_INFO "fb%d: %s frame buffer device\n",
- info->node, info->fix.id);
- return 0;
-
-err_fb_dealoc:
- fb_dealloc_cmap(&info->cmap);
-err_unmap:
- iounmap(info->screen_base);
- framebuffer_release(info);
-err_release_mem:
- release_mem_region(imacfb_fix.smem_start, size_total);
- return err;
-}
-
-static struct platform_driver imacfb_driver = {
- .probe = imacfb_probe,
- .driver = {
- .name = "imacfb",
- },
-};
-
-static struct platform_device imacfb_device = {
- .name = "imacfb",
-};
-
-static int __init imacfb_init(void)
-{
- int ret;
- char *option = NULL;
-
- if (!efi_enabled)
- return -ENODEV;
- if (!dmi_check_system(dmi_system_table))
- return -ENODEV;
- if (model == M_UNKNOWN)
- return -ENODEV;
-
- if (fb_get_options("imacfb", &option))
- return -ENODEV;
-
- imacfb_setup(option);
- ret = platform_driver_register(&imacfb_driver);
-
- if (!ret) {
- ret = platform_device_register(&imacfb_device);
- if (ret)
- platform_driver_unregister(&imacfb_driver);
- }
- return ret;
-}
-module_init(imacfb_init);
-
-MODULE_LICENSE("GPL");
--
1.5.4.3
16 years