linux-stable/drivers/media/pci/zoran/zr36016.c
Hans Verkuil 2a0c28063d media: zoran: move to mainline
The zoran driver can be moved back to mainline after it has been
converted by Corentin Labbe to vb2.

Note that the zoran driver no longer supports video output, but
video capture is working fine now.

Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
2022-08-29 15:33:45 +02:00

406 lines
11 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Zoran ZR36016 basic configuration functions
*
* Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
/* headerfile of this module */
#include "zr36016.h"
/* codec io API */
#include "videocodec.h"
/*
* it doesn't make sense to have more than 20 or so,
* just to prevent some unwanted loops
*/
#define MAX_CODECS 20
/* amount of chips attached via this driver */
static int zr36016_codecs;
/*
* Local hardware I/O functions: read/write via codec layer
* (registers are located in the master device)
*/
/* read and write functions */
static u8 zr36016_read(struct zr36016 *ptr, u16 reg)
{
u8 value = 0;
struct zoran *zr = videocodec_to_zoran(ptr->codec);
/* just in case something is wrong... */
if (ptr->codec->master_data->readreg)
value = (ptr->codec->master_data->readreg(ptr->codec, reg)) & 0xFF;
else
zrdev_err(zr, "%s: invalid I/O setup, nothing read!\n", ptr->name);
zrdev_dbg(zr, "%s: reading from 0x%04x: %02x\n", ptr->name, reg, value);
return value;
}
static void zr36016_write(struct zr36016 *ptr, u16 reg, u8 value)
{
struct zoran *zr = videocodec_to_zoran(ptr->codec);
zrdev_dbg(zr, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value, reg);
// just in case something is wrong...
if (ptr->codec->master_data->writereg)
ptr->codec->master_data->writereg(ptr->codec, reg, value);
else
zrdev_err(zr, "%s: invalid I/O setup, nothing written!\n", ptr->name);
}
/*
* indirect read and write functions
*
* the 016 supports auto-addr-increment, but
* writing it all time cost not much and is safer...
*/
static u8 zr36016_readi(struct zr36016 *ptr, u16 reg)
{
u8 value = 0;
struct zoran *zr = videocodec_to_zoran(ptr->codec);
/* just in case something is wrong... */
if ((ptr->codec->master_data->writereg) && (ptr->codec->master_data->readreg)) {
ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F);
value = (ptr->codec->master_data->readreg(ptr->codec, ZR016_IDATA)) & 0xFF;
} else {
zrdev_err(zr, "%s: invalid I/O setup, nothing read (i)!\n", ptr->name);
}
zrdev_dbg(zr, "%s: reading indirect from 0x%04x: %02x\n",
ptr->name, reg, value);
return value;
}
static void zr36016_writei(struct zr36016 *ptr, u16 reg, u8 value)
{
struct zoran *zr = videocodec_to_zoran(ptr->codec);
zrdev_dbg(zr, "%s: writing indirect 0x%02x to 0x%04x\n", ptr->name,
value, reg);
/* just in case something is wrong... */
if (ptr->codec->master_data->writereg) {
ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F);
ptr->codec->master_data->writereg(ptr->codec, ZR016_IDATA, value & 0x0FF);
} else {
zrdev_err(zr, "%s: invalid I/O setup, nothing written (i)!\n", ptr->name);
}
}
/* Local helper function: version read */
/* version kept in datastructure */
static u8 zr36016_read_version(struct zr36016 *ptr)
{
ptr->version = zr36016_read(ptr, 0) >> 4;
return ptr->version;
}
/*
* Local helper function: basic test of "connectivity", writes/reads
* to/from PAX-Lo register
*/
static int zr36016_basic_test(struct zr36016 *ptr)
{
struct zoran *zr = videocodec_to_zoran(ptr->codec);
if (*KERN_INFO <= CONSOLE_LOGLEVEL_DEFAULT) {
int i;
zr36016_writei(ptr, ZR016I_PAX_LO, 0x55);
zrdev_dbg(zr, "%s: registers: ", ptr->name);
for (i = 0; i <= 0x0b; i++)
zrdev_dbg(zr, "%02x ", zr36016_readi(ptr, i));
zrdev_dbg(zr, "\n");
}
// for testing just write 0, then the default value to a register and read
// it back in both cases
zr36016_writei(ptr, ZR016I_PAX_LO, 0x00);
if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0) {
zrdev_err(zr, "%s: attach failed, can't connect to vfe processor!\n", ptr->name);
return -ENXIO;
}
zr36016_writei(ptr, ZR016I_PAX_LO, 0x0d0);
if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0d0) {
zrdev_err(zr, "%s: attach failed, can't connect to vfe processor!\n", ptr->name);
return -ENXIO;
}
// we allow version numbers from 0-3, should be enough, though
zr36016_read_version(ptr);
if (ptr->version & 0x0c) {
zrdev_err(zr, "%s: attach failed, suspicious version %d found...\n", ptr->name,
ptr->version);
return -ENXIO;
}
return 0; /* looks good! */
}
/* Basic datasets & init */
static void zr36016_init(struct zr36016 *ptr)
{
// stop any processing
zr36016_write(ptr, ZR016_GOSTOP, 0);
// mode setup (yuv422 in and out, compression/expansuon due to mode)
zr36016_write(ptr, ZR016_MODE,
ZR016_YUV422 | ZR016_YUV422_YUV422 |
(ptr->mode == CODEC_DO_COMPRESSION ?
ZR016_COMPRESSION : ZR016_EXPANSION));
// misc setup
zr36016_writei(ptr, ZR016I_SETUP1,
(ptr->xdec ? (ZR016_HRFL | ZR016_HORZ) : 0) |
(ptr->ydec ? ZR016_VERT : 0) | ZR016_CNTI);
zr36016_writei(ptr, ZR016I_SETUP2, ZR016_CCIR);
// Window setup
// (no extra offset for now, norm defines offset, default width height)
zr36016_writei(ptr, ZR016I_PAX_HI, ptr->width >> 8);
zr36016_writei(ptr, ZR016I_PAX_LO, ptr->width & 0xFF);
zr36016_writei(ptr, ZR016I_PAY_HI, ptr->height >> 8);
zr36016_writei(ptr, ZR016I_PAY_LO, ptr->height & 0xFF);
zr36016_writei(ptr, ZR016I_NAX_HI, ptr->xoff >> 8);
zr36016_writei(ptr, ZR016I_NAX_LO, ptr->xoff & 0xFF);
zr36016_writei(ptr, ZR016I_NAY_HI, ptr->yoff >> 8);
zr36016_writei(ptr, ZR016I_NAY_LO, ptr->yoff & 0xFF);
/* shall we continue now, please? */
zr36016_write(ptr, ZR016_GOSTOP, 1);
}
/*
* CODEC API FUNCTIONS
*
* These functions are accessed by the master via the API structure
*/
/*
* set compression/expansion mode and launches codec -
* this should be the last call from the master before starting processing
*/
static int zr36016_set_mode(struct videocodec *codec, int mode)
{
struct zr36016 *ptr = (struct zr36016 *)codec->data;
struct zoran *zr = videocodec_to_zoran(codec);
zrdev_dbg(zr, "%s: set_mode %d call\n", ptr->name, mode);
if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION))
return -EINVAL;
ptr->mode = mode;
zr36016_init(ptr);
return 0;
}
/* set picture size */
static int zr36016_set_video(struct videocodec *codec, const struct tvnorm *norm,
struct vfe_settings *cap, struct vfe_polarity *pol)
{
struct zr36016 *ptr = (struct zr36016 *)codec->data;
struct zoran *zr = videocodec_to_zoran(codec);
zrdev_dbg(zr, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) call\n",
ptr->name, norm->h_start, norm->v_start,
cap->x, cap->y, cap->width, cap->height,
cap->decimation);
/*
* if () return -EINVAL;
* trust the master driver that it knows what it does - so
* we allow invalid startx/y for now ...
*/
ptr->width = cap->width;
ptr->height = cap->height;
/*
* (Ronald) This is ugly. zoran_device.c, line 387
* already mentions what happens if h_start is even
* (blue faces, etc., cr/cb inversed). There's probably
* some good reason why h_start is 0 instead of 1, so I'm
* leaving it to this for now, but really... This can be
* done a lot simpler
*/
ptr->xoff = (norm->h_start ? norm->h_start : 1) + cap->x;
/*
* Something to note here (I don't understand it), setting
* v_start too high will cause the codec to 'not work'. I
* really don't get it. values of 16 (v_start) already break
* it here. Just '0' seems to work. More testing needed!
*/
ptr->yoff = norm->v_start + cap->y;
/* (Ronald) dzjeeh, can't this thing do hor_decimation = 4? */
ptr->xdec = ((cap->decimation & 0xff) == 1) ? 0 : 1;
ptr->ydec = (((cap->decimation >> 8) & 0xff) == 1) ? 0 : 1;
return 0;
}
/* additional control functions */
static int zr36016_control(struct videocodec *codec, int type, int size, void *data)
{
struct zr36016 *ptr = (struct zr36016 *)codec->data;
struct zoran *zr = videocodec_to_zoran(codec);
int *ival = (int *)data;
zrdev_dbg(zr, "%s: control %d call with %d byte\n",
ptr->name, type, size);
switch (type) {
case CODEC_G_STATUS: /* get last status - we don't know it ... */
if (size != sizeof(int))
return -EFAULT;
*ival = 0;
break;
case CODEC_G_CODEC_MODE:
if (size != sizeof(int))
return -EFAULT;
*ival = 0;
break;
case CODEC_S_CODEC_MODE:
if (size != sizeof(int))
return -EFAULT;
if (*ival != 0)
return -EINVAL;
/* not needed, do nothing */
return 0;
case CODEC_G_VFE:
case CODEC_S_VFE:
return 0;
case CODEC_S_MMAP:
/* not available, give an error */
return -ENXIO;
default:
return -EINVAL;
}
return size;
}
/*
* Exit and unregister function:
*
* Deinitializes Zoran's JPEG processor
*/
static int zr36016_unset(struct videocodec *codec)
{
struct zr36016 *ptr = codec->data;
struct zoran *zr = videocodec_to_zoran(codec);
if (ptr) {
/* do wee need some codec deinit here, too ???? */
zrdev_dbg(zr, "%s: finished codec #%d\n", ptr->name, ptr->num);
kfree(ptr);
codec->data = NULL;
zr36016_codecs--;
return 0;
}
return -EFAULT;
}
/*
* Setup and registry function:
*
* Initializes Zoran's JPEG processor
*
* Also sets pixel size, average code size, mode (compr./decompr.)
* (the given size is determined by the processor with the video interface)
*/
static int zr36016_setup(struct videocodec *codec)
{
struct zr36016 *ptr;
struct zoran *zr = videocodec_to_zoran(codec);
int res;
zrdev_dbg(zr, "zr36016: initializing VFE subsystem #%d.\n", zr36016_codecs);
if (zr36016_codecs == MAX_CODECS) {
zrdev_err(zr, "zr36016: Can't attach more codecs!\n");
return -ENOSPC;
}
//mem structure init
ptr = kzalloc(sizeof(*ptr), GFP_KERNEL);
codec->data = ptr;
if (!ptr)
return -ENOMEM;
snprintf(ptr->name, sizeof(ptr->name), "zr36016[%d]", zr36016_codecs);
ptr->num = zr36016_codecs++;
ptr->codec = codec;
//testing
res = zr36016_basic_test(ptr);
if (res < 0) {
zr36016_unset(codec);
return res;
}
//final setup
ptr->mode = CODEC_DO_COMPRESSION;
ptr->width = 768;
ptr->height = 288;
ptr->xdec = 1;
ptr->ydec = 0;
zr36016_init(ptr);
zrdev_dbg(zr, "%s: codec v%d attached and running\n",
ptr->name, ptr->version);
return 0;
}
static const struct videocodec zr36016_codec = {
.name = "zr36016",
.magic = 0L, /* magic not used */
.flags =
CODEC_FLAG_HARDWARE | CODEC_FLAG_VFE | CODEC_FLAG_ENCODER |
CODEC_FLAG_DECODER,
.type = CODEC_TYPE_ZR36016,
.setup = zr36016_setup, /* functionality */
.unset = zr36016_unset,
.set_mode = zr36016_set_mode,
.set_video = zr36016_set_video,
.control = zr36016_control,
/* others are not used */
};
/* HOOK IN DRIVER AS KERNEL MODULE */
int zr36016_init_module(void)
{
zr36016_codecs = 0;
return videocodec_register(&zr36016_codec);
}
void zr36016_cleanup_module(void)
{
if (zr36016_codecs) {
pr_debug("zr36016: something's wrong - %d codecs left somehow.\n",
zr36016_codecs);
}
videocodec_unregister(&zr36016_codec);
}