162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Zoran ZR36016 basic configuration functions
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/* headerfile of this module */
1362306a36Sopenharmony_ci#include "zr36016.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/* codec io API */
1662306a36Sopenharmony_ci#include "videocodec.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/*
1962306a36Sopenharmony_ci * it doesn't make sense to have more than 20 or so,
2062306a36Sopenharmony_ci * just to prevent some unwanted loops
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_ci#define MAX_CODECS 20
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* amount of chips attached via this driver */
2562306a36Sopenharmony_cistatic int zr36016_codecs;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/*
2862306a36Sopenharmony_ci * Local hardware I/O functions: read/write via codec layer
2962306a36Sopenharmony_ci * (registers are located in the master device)
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/* read and write functions */
3362306a36Sopenharmony_cistatic u8 zr36016_read(struct zr36016 *ptr, u16 reg)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	u8 value = 0;
3662306a36Sopenharmony_ci	struct zoran *zr = videocodec_to_zoran(ptr->codec);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	/* just in case something is wrong... */
3962306a36Sopenharmony_ci	if (ptr->codec->master_data->readreg)
4062306a36Sopenharmony_ci		value = (ptr->codec->master_data->readreg(ptr->codec, reg)) & 0xFF;
4162306a36Sopenharmony_ci	else
4262306a36Sopenharmony_ci		zrdev_err(zr, "%s: invalid I/O setup, nothing read!\n", ptr->name);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	zrdev_dbg(zr, "%s: reading from 0x%04x: %02x\n", ptr->name, reg, value);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	return value;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic void zr36016_write(struct zr36016 *ptr, u16 reg, u8 value)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct zoran *zr = videocodec_to_zoran(ptr->codec);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	zrdev_dbg(zr, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value, reg);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	// just in case something is wrong...
5662306a36Sopenharmony_ci	if (ptr->codec->master_data->writereg)
5762306a36Sopenharmony_ci		ptr->codec->master_data->writereg(ptr->codec, reg, value);
5862306a36Sopenharmony_ci	else
5962306a36Sopenharmony_ci		zrdev_err(zr, "%s: invalid I/O setup, nothing written!\n", ptr->name);
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/*
6362306a36Sopenharmony_ci * indirect read and write functions
6462306a36Sopenharmony_ci *
6562306a36Sopenharmony_ci * the 016 supports auto-addr-increment, but
6662306a36Sopenharmony_ci * writing it all time cost not much and is safer...
6762306a36Sopenharmony_ci */
6862306a36Sopenharmony_cistatic u8 zr36016_readi(struct zr36016 *ptr, u16 reg)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	u8 value = 0;
7162306a36Sopenharmony_ci	struct zoran *zr = videocodec_to_zoran(ptr->codec);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	/* just in case something is wrong... */
7462306a36Sopenharmony_ci	if ((ptr->codec->master_data->writereg) && (ptr->codec->master_data->readreg)) {
7562306a36Sopenharmony_ci		ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F);
7662306a36Sopenharmony_ci		value = (ptr->codec->master_data->readreg(ptr->codec, ZR016_IDATA)) & 0xFF;
7762306a36Sopenharmony_ci	} else {
7862306a36Sopenharmony_ci		zrdev_err(zr, "%s: invalid I/O setup, nothing read (i)!\n", ptr->name);
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	zrdev_dbg(zr, "%s: reading indirect from 0x%04x: %02x\n",
8262306a36Sopenharmony_ci		  ptr->name, reg, value);
8362306a36Sopenharmony_ci	return value;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic void zr36016_writei(struct zr36016 *ptr, u16 reg, u8 value)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct zoran *zr = videocodec_to_zoran(ptr->codec);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	zrdev_dbg(zr, "%s: writing indirect 0x%02x to 0x%04x\n", ptr->name,
9162306a36Sopenharmony_ci		  value, reg);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/* just in case something is wrong... */
9462306a36Sopenharmony_ci	if (ptr->codec->master_data->writereg) {
9562306a36Sopenharmony_ci		ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F);
9662306a36Sopenharmony_ci		ptr->codec->master_data->writereg(ptr->codec, ZR016_IDATA, value & 0x0FF);
9762306a36Sopenharmony_ci	} else {
9862306a36Sopenharmony_ci		zrdev_err(zr, "%s: invalid I/O setup, nothing written (i)!\n", ptr->name);
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/* Local helper function: version read */
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/* version kept in datastructure */
10562306a36Sopenharmony_cistatic u8 zr36016_read_version(struct zr36016 *ptr)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	ptr->version = zr36016_read(ptr, 0) >> 4;
10862306a36Sopenharmony_ci	return ptr->version;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/*
11262306a36Sopenharmony_ci * Local helper function: basic test of "connectivity", writes/reads
11362306a36Sopenharmony_ci * to/from PAX-Lo register
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic int zr36016_basic_test(struct zr36016 *ptr)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct zoran *zr = videocodec_to_zoran(ptr->codec);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (*KERN_INFO <= CONSOLE_LOGLEVEL_DEFAULT) {
12162306a36Sopenharmony_ci		int i;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci		zr36016_writei(ptr, ZR016I_PAX_LO, 0x55);
12462306a36Sopenharmony_ci		zrdev_dbg(zr, "%s: registers: ", ptr->name);
12562306a36Sopenharmony_ci		for (i = 0; i <= 0x0b; i++)
12662306a36Sopenharmony_ci			zrdev_dbg(zr, "%02x ", zr36016_readi(ptr, i));
12762306a36Sopenharmony_ci		zrdev_dbg(zr, "\n");
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci	// for testing just write 0, then the default value to a register and read
13062306a36Sopenharmony_ci	// it back in both cases
13162306a36Sopenharmony_ci	zr36016_writei(ptr, ZR016I_PAX_LO, 0x00);
13262306a36Sopenharmony_ci	if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0) {
13362306a36Sopenharmony_ci		zrdev_err(zr, "%s: attach failed, can't connect to vfe processor!\n", ptr->name);
13462306a36Sopenharmony_ci		return -ENXIO;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci	zr36016_writei(ptr, ZR016I_PAX_LO, 0x0d0);
13762306a36Sopenharmony_ci	if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0d0) {
13862306a36Sopenharmony_ci		zrdev_err(zr, "%s: attach failed, can't connect to vfe processor!\n", ptr->name);
13962306a36Sopenharmony_ci		return -ENXIO;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci	// we allow version numbers from 0-3, should be enough, though
14262306a36Sopenharmony_ci	zr36016_read_version(ptr);
14362306a36Sopenharmony_ci	if (ptr->version & 0x0c) {
14462306a36Sopenharmony_ci		zrdev_err(zr, "%s: attach failed, suspicious version %d found...\n", ptr->name,
14562306a36Sopenharmony_ci			  ptr->version);
14662306a36Sopenharmony_ci		return -ENXIO;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return 0;		/* looks good! */
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci/* Basic datasets & init */
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic void zr36016_init(struct zr36016 *ptr)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	// stop any processing
15762306a36Sopenharmony_ci	zr36016_write(ptr, ZR016_GOSTOP, 0);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	// mode setup (yuv422 in and out, compression/expansuon due to mode)
16062306a36Sopenharmony_ci	zr36016_write(ptr, ZR016_MODE,
16162306a36Sopenharmony_ci		      ZR016_YUV422 | ZR016_YUV422_YUV422 |
16262306a36Sopenharmony_ci		      (ptr->mode == CODEC_DO_COMPRESSION ?
16362306a36Sopenharmony_ci		       ZR016_COMPRESSION : ZR016_EXPANSION));
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	// misc setup
16662306a36Sopenharmony_ci	zr36016_writei(ptr, ZR016I_SETUP1,
16762306a36Sopenharmony_ci		       (ptr->xdec ? (ZR016_HRFL | ZR016_HORZ) : 0) |
16862306a36Sopenharmony_ci		       (ptr->ydec ? ZR016_VERT : 0) | ZR016_CNTI);
16962306a36Sopenharmony_ci	zr36016_writei(ptr, ZR016I_SETUP2, ZR016_CCIR);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	// Window setup
17262306a36Sopenharmony_ci	// (no extra offset for now, norm defines offset, default width height)
17362306a36Sopenharmony_ci	zr36016_writei(ptr, ZR016I_PAX_HI, ptr->width >> 8);
17462306a36Sopenharmony_ci	zr36016_writei(ptr, ZR016I_PAX_LO, ptr->width & 0xFF);
17562306a36Sopenharmony_ci	zr36016_writei(ptr, ZR016I_PAY_HI, ptr->height >> 8);
17662306a36Sopenharmony_ci	zr36016_writei(ptr, ZR016I_PAY_LO, ptr->height & 0xFF);
17762306a36Sopenharmony_ci	zr36016_writei(ptr, ZR016I_NAX_HI, ptr->xoff >> 8);
17862306a36Sopenharmony_ci	zr36016_writei(ptr, ZR016I_NAX_LO, ptr->xoff & 0xFF);
17962306a36Sopenharmony_ci	zr36016_writei(ptr, ZR016I_NAY_HI, ptr->yoff >> 8);
18062306a36Sopenharmony_ci	zr36016_writei(ptr, ZR016I_NAY_LO, ptr->yoff & 0xFF);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* shall we continue now, please? */
18362306a36Sopenharmony_ci	zr36016_write(ptr, ZR016_GOSTOP, 1);
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/*
18762306a36Sopenharmony_ci * CODEC API FUNCTIONS
18862306a36Sopenharmony_ci *
18962306a36Sopenharmony_ci * These functions are accessed by the master via the API structure
19062306a36Sopenharmony_ci */
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci/*
19362306a36Sopenharmony_ci * set compression/expansion mode and launches codec -
19462306a36Sopenharmony_ci * this should be the last call from the master before starting processing
19562306a36Sopenharmony_ci */
19662306a36Sopenharmony_cistatic int zr36016_set_mode(struct videocodec *codec, int mode)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct zr36016 *ptr = (struct zr36016 *)codec->data;
19962306a36Sopenharmony_ci	struct zoran *zr = videocodec_to_zoran(codec);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	zrdev_dbg(zr, "%s: set_mode %d call\n", ptr->name, mode);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION))
20462306a36Sopenharmony_ci		return -EINVAL;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	ptr->mode = mode;
20762306a36Sopenharmony_ci	zr36016_init(ptr);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	return 0;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci/* set picture size */
21362306a36Sopenharmony_cistatic int zr36016_set_video(struct videocodec *codec, const struct tvnorm *norm,
21462306a36Sopenharmony_ci			     struct vfe_settings *cap, struct vfe_polarity *pol)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct zr36016 *ptr = (struct zr36016 *)codec->data;
21762306a36Sopenharmony_ci	struct zoran *zr = videocodec_to_zoran(codec);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	zrdev_dbg(zr, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) call\n",
22062306a36Sopenharmony_ci		  ptr->name, norm->h_start, norm->v_start,
22162306a36Sopenharmony_ci		  cap->x, cap->y, cap->width, cap->height,
22262306a36Sopenharmony_ci		  cap->decimation);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	/*
22562306a36Sopenharmony_ci	 * if () return -EINVAL;
22662306a36Sopenharmony_ci	 * trust the master driver that it knows what it does - so
22762306a36Sopenharmony_ci	 * we allow invalid startx/y for now ...
22862306a36Sopenharmony_ci	 */
22962306a36Sopenharmony_ci	ptr->width = cap->width;
23062306a36Sopenharmony_ci	ptr->height = cap->height;
23162306a36Sopenharmony_ci	/*
23262306a36Sopenharmony_ci	 * (Ronald) This is ugly. zoran_device.c, line 387
23362306a36Sopenharmony_ci	 * already mentions what happens if h_start is even
23462306a36Sopenharmony_ci	 * (blue faces, etc., cr/cb inversed). There's probably
23562306a36Sopenharmony_ci	 * some good reason why h_start is 0 instead of 1, so I'm
23662306a36Sopenharmony_ci	 * leaving it to this for now, but really... This can be
23762306a36Sopenharmony_ci	 * done a lot simpler
23862306a36Sopenharmony_ci	 */
23962306a36Sopenharmony_ci	ptr->xoff = (norm->h_start ? norm->h_start : 1) + cap->x;
24062306a36Sopenharmony_ci	/*
24162306a36Sopenharmony_ci	 * Something to note here (I don't understand it), setting
24262306a36Sopenharmony_ci	 * v_start too high will cause the codec to 'not work'. I
24362306a36Sopenharmony_ci	 * really don't get it. values of 16 (v_start) already break
24462306a36Sopenharmony_ci	 * it here. Just '0' seems to work. More testing needed!
24562306a36Sopenharmony_ci	 */
24662306a36Sopenharmony_ci	ptr->yoff = norm->v_start + cap->y;
24762306a36Sopenharmony_ci	/* (Ronald) dzjeeh, can't this thing do hor_decimation = 4? */
24862306a36Sopenharmony_ci	ptr->xdec = ((cap->decimation & 0xff) == 1) ? 0 : 1;
24962306a36Sopenharmony_ci	ptr->ydec = (((cap->decimation >> 8) & 0xff) == 1) ? 0 : 1;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return 0;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci/* additional control functions */
25562306a36Sopenharmony_cistatic int zr36016_control(struct videocodec *codec, int type, int size, void *data)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	struct zr36016 *ptr = (struct zr36016 *)codec->data;
25862306a36Sopenharmony_ci	struct zoran *zr = videocodec_to_zoran(codec);
25962306a36Sopenharmony_ci	int *ival = (int *)data;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	zrdev_dbg(zr, "%s: control %d call with %d byte\n",
26262306a36Sopenharmony_ci		  ptr->name, type, size);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	switch (type) {
26562306a36Sopenharmony_ci	case CODEC_G_STATUS:	/* get last status - we don't know it ... */
26662306a36Sopenharmony_ci		if (size != sizeof(int))
26762306a36Sopenharmony_ci			return -EFAULT;
26862306a36Sopenharmony_ci		*ival = 0;
26962306a36Sopenharmony_ci		break;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	case CODEC_G_CODEC_MODE:
27262306a36Sopenharmony_ci		if (size != sizeof(int))
27362306a36Sopenharmony_ci			return -EFAULT;
27462306a36Sopenharmony_ci		*ival = 0;
27562306a36Sopenharmony_ci		break;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	case CODEC_S_CODEC_MODE:
27862306a36Sopenharmony_ci		if (size != sizeof(int))
27962306a36Sopenharmony_ci			return -EFAULT;
28062306a36Sopenharmony_ci		if (*ival != 0)
28162306a36Sopenharmony_ci			return -EINVAL;
28262306a36Sopenharmony_ci		/* not needed, do nothing */
28362306a36Sopenharmony_ci		return 0;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	case CODEC_G_VFE:
28662306a36Sopenharmony_ci	case CODEC_S_VFE:
28762306a36Sopenharmony_ci		return 0;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	case CODEC_S_MMAP:
29062306a36Sopenharmony_ci		/* not available, give an error */
29162306a36Sopenharmony_ci		return -ENXIO;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	default:
29462306a36Sopenharmony_ci		return -EINVAL;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	return size;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci/*
30162306a36Sopenharmony_ci * Exit and unregister function:
30262306a36Sopenharmony_ci *
30362306a36Sopenharmony_ci * Deinitializes Zoran's JPEG processor
30462306a36Sopenharmony_ci */
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic int zr36016_unset(struct videocodec *codec)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct zr36016 *ptr = codec->data;
30962306a36Sopenharmony_ci	struct zoran *zr = videocodec_to_zoran(codec);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (ptr) {
31262306a36Sopenharmony_ci		/* do wee need some codec deinit here, too ???? */
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci		zrdev_dbg(zr, "%s: finished codec #%d\n", ptr->name, ptr->num);
31562306a36Sopenharmony_ci		kfree(ptr);
31662306a36Sopenharmony_ci		codec->data = NULL;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		zr36016_codecs--;
31962306a36Sopenharmony_ci		return 0;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	return -EFAULT;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci/*
32662306a36Sopenharmony_ci * Setup and registry function:
32762306a36Sopenharmony_ci *
32862306a36Sopenharmony_ci * Initializes Zoran's JPEG processor
32962306a36Sopenharmony_ci *
33062306a36Sopenharmony_ci * Also sets pixel size, average code size, mode (compr./decompr.)
33162306a36Sopenharmony_ci * (the given size is determined by the processor with the video interface)
33262306a36Sopenharmony_ci */
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic int zr36016_setup(struct videocodec *codec)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct zr36016 *ptr;
33762306a36Sopenharmony_ci	struct zoran *zr = videocodec_to_zoran(codec);
33862306a36Sopenharmony_ci	int res;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	zrdev_dbg(zr, "zr36016: initializing VFE subsystem #%d.\n", zr36016_codecs);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	if (zr36016_codecs == MAX_CODECS) {
34362306a36Sopenharmony_ci		zrdev_err(zr, "zr36016: Can't attach more codecs!\n");
34462306a36Sopenharmony_ci		return -ENOSPC;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci	//mem structure init
34762306a36Sopenharmony_ci	ptr = kzalloc(sizeof(*ptr), GFP_KERNEL);
34862306a36Sopenharmony_ci	codec->data = ptr;
34962306a36Sopenharmony_ci	if (!ptr)
35062306a36Sopenharmony_ci		return -ENOMEM;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	snprintf(ptr->name, sizeof(ptr->name), "zr36016[%d]", zr36016_codecs);
35362306a36Sopenharmony_ci	ptr->num = zr36016_codecs++;
35462306a36Sopenharmony_ci	ptr->codec = codec;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	//testing
35762306a36Sopenharmony_ci	res = zr36016_basic_test(ptr);
35862306a36Sopenharmony_ci	if (res < 0) {
35962306a36Sopenharmony_ci		zr36016_unset(codec);
36062306a36Sopenharmony_ci		return res;
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci	//final setup
36362306a36Sopenharmony_ci	ptr->mode = CODEC_DO_COMPRESSION;
36462306a36Sopenharmony_ci	ptr->width = 768;
36562306a36Sopenharmony_ci	ptr->height = 288;
36662306a36Sopenharmony_ci	ptr->xdec = 1;
36762306a36Sopenharmony_ci	ptr->ydec = 0;
36862306a36Sopenharmony_ci	zr36016_init(ptr);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	zrdev_dbg(zr, "%s: codec v%d attached and running\n",
37162306a36Sopenharmony_ci		  ptr->name, ptr->version);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	return 0;
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic const struct videocodec zr36016_codec = {
37762306a36Sopenharmony_ci	.name = "zr36016",
37862306a36Sopenharmony_ci	.magic = 0L,		/* magic not used */
37962306a36Sopenharmony_ci	.flags =
38062306a36Sopenharmony_ci	    CODEC_FLAG_HARDWARE | CODEC_FLAG_VFE | CODEC_FLAG_ENCODER |
38162306a36Sopenharmony_ci	    CODEC_FLAG_DECODER,
38262306a36Sopenharmony_ci	.type = CODEC_TYPE_ZR36016,
38362306a36Sopenharmony_ci	.setup = zr36016_setup,	/* functionality */
38462306a36Sopenharmony_ci	.unset = zr36016_unset,
38562306a36Sopenharmony_ci	.set_mode = zr36016_set_mode,
38662306a36Sopenharmony_ci	.set_video = zr36016_set_video,
38762306a36Sopenharmony_ci	.control = zr36016_control,
38862306a36Sopenharmony_ci	/* others are not used */
38962306a36Sopenharmony_ci};
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci/* HOOK IN DRIVER AS KERNEL MODULE */
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ciint zr36016_init_module(void)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	zr36016_codecs = 0;
39662306a36Sopenharmony_ci	return videocodec_register(&zr36016_codec);
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_civoid zr36016_cleanup_module(void)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	if (zr36016_codecs) {
40262306a36Sopenharmony_ci		pr_debug("zr36016: something's wrong - %d codecs left somehow.\n",
40362306a36Sopenharmony_ci			 zr36016_codecs);
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci	videocodec_unregister(&zr36016_codec);
40662306a36Sopenharmony_ci}
407