407 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Zoran ZR36016 basic configuration functions4 *5 * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>6 */7 8#include <linux/module.h>9#include <linux/init.h>10#include <linux/slab.h>11 12/* headerfile of this module */13#include "zr36016.h"14 15/* codec io API */16#include "videocodec.h"17 18/*19 * it doesn't make sense to have more than 20 or so,20 * just to prevent some unwanted loops21 */22#define MAX_CODECS 2023 24/* amount of chips attached via this driver */25static int zr36016_codecs;26 27/*28 * Local hardware I/O functions: read/write via codec layer29 * (registers are located in the master device)30 */31 32/* read and write functions */33static u8 zr36016_read(struct zr36016 *ptr, u16 reg)34{35 u8 value = 0;36 struct zoran *zr = videocodec_to_zoran(ptr->codec);37 38 /* just in case something is wrong... */39 if (ptr->codec->master_data->readreg)40 value = (ptr->codec->master_data->readreg(ptr->codec, reg)) & 0xFF;41 else42 zrdev_err(zr, "%s: invalid I/O setup, nothing read!\n", ptr->name);43 44 zrdev_dbg(zr, "%s: reading from 0x%04x: %02x\n", ptr->name, reg, value);45 46 return value;47}48 49static void zr36016_write(struct zr36016 *ptr, u16 reg, u8 value)50{51 struct zoran *zr = videocodec_to_zoran(ptr->codec);52 53 zrdev_dbg(zr, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value, reg);54 55 // just in case something is wrong...56 if (ptr->codec->master_data->writereg)57 ptr->codec->master_data->writereg(ptr->codec, reg, value);58 else59 zrdev_err(zr, "%s: invalid I/O setup, nothing written!\n", ptr->name);60}61 62/*63 * indirect read and write functions64 *65 * the 016 supports auto-addr-increment, but66 * writing it all time cost not much and is safer...67 */68static u8 zr36016_readi(struct zr36016 *ptr, u16 reg)69{70 u8 value = 0;71 struct zoran *zr = videocodec_to_zoran(ptr->codec);72 73 /* just in case something is wrong... */74 if ((ptr->codec->master_data->writereg) && (ptr->codec->master_data->readreg)) {75 ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F);76 value = (ptr->codec->master_data->readreg(ptr->codec, ZR016_IDATA)) & 0xFF;77 } else {78 zrdev_err(zr, "%s: invalid I/O setup, nothing read (i)!\n", ptr->name);79 }80 81 zrdev_dbg(zr, "%s: reading indirect from 0x%04x: %02x\n",82 ptr->name, reg, value);83 return value;84}85 86static void zr36016_writei(struct zr36016 *ptr, u16 reg, u8 value)87{88 struct zoran *zr = videocodec_to_zoran(ptr->codec);89 90 zrdev_dbg(zr, "%s: writing indirect 0x%02x to 0x%04x\n", ptr->name,91 value, reg);92 93 /* just in case something is wrong... */94 if (ptr->codec->master_data->writereg) {95 ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F);96 ptr->codec->master_data->writereg(ptr->codec, ZR016_IDATA, value & 0x0FF);97 } else {98 zrdev_err(zr, "%s: invalid I/O setup, nothing written (i)!\n", ptr->name);99 }100}101 102/* Local helper function: version read */103 104/* version kept in datastructure */105static u8 zr36016_read_version(struct zr36016 *ptr)106{107 ptr->version = zr36016_read(ptr, 0) >> 4;108 return ptr->version;109}110 111/*112 * Local helper function: basic test of "connectivity", writes/reads113 * to/from PAX-Lo register114 */115 116static int zr36016_basic_test(struct zr36016 *ptr)117{118 struct zoran *zr = videocodec_to_zoran(ptr->codec);119 120 if (*KERN_INFO <= CONSOLE_LOGLEVEL_DEFAULT) {121 int i;122 123 zr36016_writei(ptr, ZR016I_PAX_LO, 0x55);124 zrdev_dbg(zr, "%s: registers: ", ptr->name);125 for (i = 0; i <= 0x0b; i++)126 zrdev_dbg(zr, "%02x ", zr36016_readi(ptr, i));127 zrdev_dbg(zr, "\n");128 }129 // for testing just write 0, then the default value to a register and read130 // it back in both cases131 zr36016_writei(ptr, ZR016I_PAX_LO, 0x00);132 if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0) {133 zrdev_err(zr, "%s: attach failed, can't connect to vfe processor!\n", ptr->name);134 return -ENXIO;135 }136 zr36016_writei(ptr, ZR016I_PAX_LO, 0x0d0);137 if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0d0) {138 zrdev_err(zr, "%s: attach failed, can't connect to vfe processor!\n", ptr->name);139 return -ENXIO;140 }141 // we allow version numbers from 0-3, should be enough, though142 zr36016_read_version(ptr);143 if (ptr->version & 0x0c) {144 zrdev_err(zr, "%s: attach failed, suspicious version %d found...\n", ptr->name,145 ptr->version);146 return -ENXIO;147 }148 149 return 0; /* looks good! */150}151 152/* Basic datasets & init */153 154static void zr36016_init(struct zr36016 *ptr)155{156 // stop any processing157 zr36016_write(ptr, ZR016_GOSTOP, 0);158 159 // mode setup (yuv422 in and out, compression/expansuon due to mode)160 zr36016_write(ptr, ZR016_MODE,161 ZR016_YUV422 | ZR016_YUV422_YUV422 |162 (ptr->mode == CODEC_DO_COMPRESSION ?163 ZR016_COMPRESSION : ZR016_EXPANSION));164 165 // misc setup166 zr36016_writei(ptr, ZR016I_SETUP1,167 (ptr->xdec ? (ZR016_HRFL | ZR016_HORZ) : 0) |168 (ptr->ydec ? ZR016_VERT : 0) | ZR016_CNTI);169 zr36016_writei(ptr, ZR016I_SETUP2, ZR016_CCIR);170 171 // Window setup172 // (no extra offset for now, norm defines offset, default width height)173 zr36016_writei(ptr, ZR016I_PAX_HI, ptr->width >> 8);174 zr36016_writei(ptr, ZR016I_PAX_LO, ptr->width & 0xFF);175 zr36016_writei(ptr, ZR016I_PAY_HI, ptr->height >> 8);176 zr36016_writei(ptr, ZR016I_PAY_LO, ptr->height & 0xFF);177 zr36016_writei(ptr, ZR016I_NAX_HI, ptr->xoff >> 8);178 zr36016_writei(ptr, ZR016I_NAX_LO, ptr->xoff & 0xFF);179 zr36016_writei(ptr, ZR016I_NAY_HI, ptr->yoff >> 8);180 zr36016_writei(ptr, ZR016I_NAY_LO, ptr->yoff & 0xFF);181 182 /* shall we continue now, please? */183 zr36016_write(ptr, ZR016_GOSTOP, 1);184}185 186/*187 * CODEC API FUNCTIONS188 *189 * These functions are accessed by the master via the API structure190 */191 192/*193 * set compression/expansion mode and launches codec -194 * this should be the last call from the master before starting processing195 */196static int zr36016_set_mode(struct videocodec *codec, int mode)197{198 struct zr36016 *ptr = (struct zr36016 *)codec->data;199 struct zoran *zr = videocodec_to_zoran(codec);200 201 zrdev_dbg(zr, "%s: set_mode %d call\n", ptr->name, mode);202 203 if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION))204 return -EINVAL;205 206 ptr->mode = mode;207 zr36016_init(ptr);208 209 return 0;210}211 212/* set picture size */213static int zr36016_set_video(struct videocodec *codec, const struct tvnorm *norm,214 struct vfe_settings *cap, struct vfe_polarity *pol)215{216 struct zr36016 *ptr = (struct zr36016 *)codec->data;217 struct zoran *zr = videocodec_to_zoran(codec);218 219 zrdev_dbg(zr, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) call\n",220 ptr->name, norm->h_start, norm->v_start,221 cap->x, cap->y, cap->width, cap->height,222 cap->decimation);223 224 /*225 * if () return -EINVAL;226 * trust the master driver that it knows what it does - so227 * we allow invalid startx/y for now ...228 */229 ptr->width = cap->width;230 ptr->height = cap->height;231 /*232 * (Ronald) This is ugly. zoran_device.c, line 387233 * already mentions what happens if h_start is even234 * (blue faces, etc., cr/cb inversed). There's probably235 * some good reason why h_start is 0 instead of 1, so I'm236 * leaving it to this for now, but really... This can be237 * done a lot simpler238 */239 ptr->xoff = (norm->h_start ? norm->h_start : 1) + cap->x;240 /*241 * Something to note here (I don't understand it), setting242 * v_start too high will cause the codec to 'not work'. I243 * really don't get it. values of 16 (v_start) already break244 * it here. Just '0' seems to work. More testing needed!245 */246 ptr->yoff = norm->v_start + cap->y;247 /* (Ronald) dzjeeh, can't this thing do hor_decimation = 4? */248 ptr->xdec = ((cap->decimation & 0xff) == 1) ? 0 : 1;249 ptr->ydec = (((cap->decimation >> 8) & 0xff) == 1) ? 0 : 1;250 251 return 0;252}253 254/* additional control functions */255static int zr36016_control(struct videocodec *codec, int type, int size, void *data)256{257 struct zr36016 *ptr = (struct zr36016 *)codec->data;258 struct zoran *zr = videocodec_to_zoran(codec);259 int *ival = (int *)data;260 261 zrdev_dbg(zr, "%s: control %d call with %d byte\n",262 ptr->name, type, size);263 264 switch (type) {265 case CODEC_G_STATUS: /* get last status - we don't know it ... */266 if (size != sizeof(int))267 return -EFAULT;268 *ival = 0;269 break;270 271 case CODEC_G_CODEC_MODE:272 if (size != sizeof(int))273 return -EFAULT;274 *ival = 0;275 break;276 277 case CODEC_S_CODEC_MODE:278 if (size != sizeof(int))279 return -EFAULT;280 if (*ival != 0)281 return -EINVAL;282 /* not needed, do nothing */283 return 0;284 285 case CODEC_G_VFE:286 case CODEC_S_VFE:287 return 0;288 289 case CODEC_S_MMAP:290 /* not available, give an error */291 return -ENXIO;292 293 default:294 return -EINVAL;295 }296 297 return size;298}299 300/*301 * Exit and unregister function:302 *303 * Deinitializes Zoran's JPEG processor304 */305 306static int zr36016_unset(struct videocodec *codec)307{308 struct zr36016 *ptr = codec->data;309 struct zoran *zr = videocodec_to_zoran(codec);310 311 if (ptr) {312 /* do wee need some codec deinit here, too ???? */313 314 zrdev_dbg(zr, "%s: finished codec #%d\n", ptr->name, ptr->num);315 kfree(ptr);316 codec->data = NULL;317 318 zr36016_codecs--;319 return 0;320 }321 322 return -EFAULT;323}324 325/*326 * Setup and registry function:327 *328 * Initializes Zoran's JPEG processor329 *330 * Also sets pixel size, average code size, mode (compr./decompr.)331 * (the given size is determined by the processor with the video interface)332 */333 334static int zr36016_setup(struct videocodec *codec)335{336 struct zr36016 *ptr;337 struct zoran *zr = videocodec_to_zoran(codec);338 int res;339 340 zrdev_dbg(zr, "zr36016: initializing VFE subsystem #%d.\n", zr36016_codecs);341 342 if (zr36016_codecs == MAX_CODECS) {343 zrdev_err(zr, "zr36016: Can't attach more codecs!\n");344 return -ENOSPC;345 }346 //mem structure init347 ptr = kzalloc(sizeof(*ptr), GFP_KERNEL);348 codec->data = ptr;349 if (!ptr)350 return -ENOMEM;351 352 snprintf(ptr->name, sizeof(ptr->name), "zr36016[%d]", zr36016_codecs);353 ptr->num = zr36016_codecs++;354 ptr->codec = codec;355 356 //testing357 res = zr36016_basic_test(ptr);358 if (res < 0) {359 zr36016_unset(codec);360 return res;361 }362 //final setup363 ptr->mode = CODEC_DO_COMPRESSION;364 ptr->width = 768;365 ptr->height = 288;366 ptr->xdec = 1;367 ptr->ydec = 0;368 zr36016_init(ptr);369 370 zrdev_dbg(zr, "%s: codec v%d attached and running\n",371 ptr->name, ptr->version);372 373 return 0;374}375 376static const struct videocodec zr36016_codec = {377 .name = "zr36016",378 .magic = 0L, /* magic not used */379 .flags =380 CODEC_FLAG_HARDWARE | CODEC_FLAG_VFE | CODEC_FLAG_ENCODER |381 CODEC_FLAG_DECODER,382 .type = CODEC_TYPE_ZR36016,383 .setup = zr36016_setup, /* functionality */384 .unset = zr36016_unset,385 .set_mode = zr36016_set_mode,386 .set_video = zr36016_set_video,387 .control = zr36016_control,388 /* others are not used */389};390 391/* HOOK IN DRIVER AS KERNEL MODULE */392 393int zr36016_init_module(void)394{395 zr36016_codecs = 0;396 return videocodec_register(&zr36016_codec);397}398 399void zr36016_cleanup_module(void)400{401 if (zr36016_codecs) {402 pr_debug("zr36016: something's wrong - %d codecs left somehow.\n",403 zr36016_codecs);404 }405 videocodec_unregister(&zr36016_codec);406}407