circbuf.c 35 KB
Newer Older
1 2
/** @file circbuf.c
 *
3
 * @brief Drivers to manipulate large circular buffer that holds compressed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 * images/video. Buffer frame data is filled in by the FPGA, frame pointers and
 * essential frames metadata filled during servicing of the interrupts.
 *
 * Copyright (C) 2016 Elphel, Inc
 *
 *  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 3 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, see <http://www.gnu.org/licenses/>.
 * -----------------------------------------------------------------------------**
 */
Mikhail Karpenko's avatar
Mikhail Karpenko committed
23

24
//#define DEBUG 1
25 26
#include <linux/device.h>

Mikhail Karpenko's avatar
Mikhail Karpenko committed
27 28 29 30 31 32 33
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
34
//#include <linux/string.h>
Mikhail Karpenko's avatar
Mikhail Karpenko committed
35
#include <linux/init.h>
36
//#include <linux/time.h>
Mikhail Karpenko's avatar
Mikhail Karpenko committed
37
#include <linux/wait.h>
38
#include <linux/dma-mapping.h>
39 40 41
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
Mikhail Karpenko's avatar
Mikhail Karpenko committed
42 43 44 45

//#include <asm/system.h>
//#include <asm/arch/memmap.h>
//#include <asm/svinto.h> obsolete
46
//#include <asm/io.h>
Mikhail Karpenko's avatar
Mikhail Karpenko committed
47 48 49 50 51 52 53 54

/*#include <asm/arch/dma.h>
#include <asm/arch/hwregs/dma_defs.h>
#include <asm/arch/hwregs/dma.h>
#include <asm/arch/hwregs/reg_map.h>
#include <asm/arch/hwregs/bif_dma_defs.h>
*/

55 56
//#include <asm/irq.h>
//#include <asm/atomic.h>
Mikhail Karpenko's avatar
Mikhail Karpenko committed
57 58


59
//#include <asm/delay.h>
Mikhail Karpenko's avatar
Mikhail Karpenko committed
60 61 62
#include <asm/uaccess.h>
#include <elphel/driver_numbers.h>
#include <elphel/c313a.h>
63
#include <elphel/elphel393-mem.h>
Mikhail Karpenko's avatar
Mikhail Karpenko committed
64 65 66 67 68 69 70

#include "framepars.h" // just for ELPHEL_DEBUG bit mask

#include "sensor_common.h"
#include "jpeghead.h"
#include "circbuf.h"
#include "exif.h"
71
#include "x393_macro.h"
72 73
//#include "x393.h"
#include "x393_helpers.h"
74

75 76 77 78 79
#define CIRCBUF_DRIVER_NAME "circbuf driver"

/** Wait queue for the processes waiting for a new frame to appear in the circular buffer */
wait_queue_head_t circbuf_wait_queue;

80
struct circbuf_priv_t circbuf_priv[SENSOR_PORTS];
81 82 83
struct circbuf_priv_t *circbuf_priv_ptr = circbuf_priv;

static struct device *g_dev_ptr;
84

85
static const struct of_device_id elphel393_circbuf_of_match[];
Mikhail Karpenko's avatar
Mikhail Karpenko committed
86

87 88 89 90 91 92
unsigned long *ccam_dma_buf_ptr[SENSOR_PORTS] = {NULL};

/* debug code */
//extern long long zero_counter[IMAGE_CHN_NUM];
/* end of debug code*/

93 94 95
int init_ccam_dma_buf_ptr(struct platform_device *pdev)
{
	int i;
96 97 98
	dma_addr_t dma_handle;
	const size_t dma_size = (CCAM_DMA_SIZE + (PAGE_SIZE >> 2)) * sizeof(int);
	struct device *dev = &pdev->dev;
99
	unsigned long *dma_buf_ptr = NULL;
100

101
	// use Elphel_buf if it was allocated
102
	if (pElphel_buf != NULL) {
103
		dma_buf_ptr = pElphel_buf->vaddr;
104
		dma_handle = pElphel_buf->paddr;
105
		dev_info(dev, "using %lu bytes of DMA memory from pElphel_buf at address 0x%08x", pElphel_buf->size * PAGE_SIZE, dma_handle);
106
	} else {
107 108
		dma_buf_ptr = dmam_alloc_coherent(dev, dma_size, &dma_handle, GFP_KERNEL);
		if (!dma_buf_ptr) {
109 110 111
			dev_err(dev, "unable to allocate DMA buffer\n");
			return -ENOMEM;
		} else {
112
			dev_info(dev, "%d bytes of DMA memory allocated at address 0x%08x", dma_size , dma_handle);
113 114 115
		}
	}

116 117
	for (i = 0; i < SENSOR_PORTS; i++) {
		circbuf_priv[i].buf_ptr = dma_buf_ptr + BYTE2DW(CIRCBUF_START_OFFSET + i * CCAM_DMA_SIZE);
118
		circbuf_priv[i].phys_addr = dma_handle + CIRCBUF_START_OFFSET + i * CCAM_DMA_SIZE;
119 120 121
		ccam_dma_buf_ptr[i] = circbuf_priv[i].buf_ptr;
		// set circular buffer size in bytes
		set_globalParam(i, G_CIRCBUFSIZE, CCAM_DMA_SIZE);
122 123 124
	}

	return 0;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
125
}
126 127 128 129

int circbuf_all_open(struct inode *inode, struct file *filp)
{
	int res;
130 131
	unsigned int minor = MINOR(inode->i_rdev);
	unsigned int dev_type;
132
	dev_dbg(g_dev_ptr, "circbuf_all_open, minor = 0x%x\n", minor);
133

134 135 136 137
	minor_to_chn(minor, &dev_type);
	switch (dev_type) {
	case CIRCBUF_MINOR:
		res = circbuf_open(inode, filp);
138
		break;
139 140
	case JPEGHEAD_MINOR:
		res = jpeghead_open(inode, filp);
141
		break;
142 143
	case HUFFMAN_MINOR:
		res = huffman_open(inode, filp);
144 145 146 147 148
		break;
	default:
		return -EINVAL;
	}
	return res;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
149
}
150

151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
int circbuf_all_release(struct inode *inode, struct file *filp)
{
	int res=0;
	unsigned int minor = MINOR(inode->i_rdev);
	unsigned int dev_type;
	dev_dbg(g_dev_ptr, "minor = 0x%x\n", minor);

	minor_to_chn(minor, &dev_type);
	switch (dev_type) {
	case CIRCBUF_MINOR:
		//        res=circbuf_release(inode,filp);
		break;
	case JPEGHEAD_MINOR:
		//        res=jpeghead_release(inode,filp);
		break;
	case HUFFMAN_MINOR:
		//        res=huffman_release(inode,filp);
		break;
	default:
		return -EINVAL;
	}
	if (filp->private_data) kfree(filp->private_data);
	return res;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
174 175
}

176
loff_t circbuf_all_lseek(struct file *file, loff_t offset, int orig)
177 178
{
	int rp;
179 180 181 182
	struct interframe_params_t *fp = NULL;
	unsigned int minor = MINOR(file->f_inode->i_rdev);
	unsigned int dev_type;
	unsigned int chn = minor_to_chn(minor, &dev_type);
183
	dev_dbg(g_dev_ptr, "circbuf_all_lseek, minor = 0x%x\n", minor);
184 185 186

	switch (dev_type) {
	case CIRCBUF_MINOR:
187
		return  circbuf_lseek(file, offset, orig);
188
	case JPEGHEAD_MINOR:
189
		if (orig == SEEK_END && offset > 0) {
190 191
			rp = BYTE2DW(X393_BUFFSUB(offset, CHUNK_SIZE)) & (~7); // convert to index to long, align to 32-bytes
			fp = (struct interframe_params_t *) &circbuf_priv[chn].buf_ptr[rp];
192 193
		}
		return  jpeghead_lseek(file, offset, orig, fp);
194
	case HUFFMAN_MINOR:
195 196 197 198
		return  huffman_lseek(file, offset, orig);
	default:
		return -EINVAL;
	}
Mikhail Karpenko's avatar
Mikhail Karpenko committed
199 200
}

201 202 203 204 205
ssize_t circbuf_all_read(struct file *file, char *buf, size_t count, loff_t *off)
{
	unsigned int minor = MINOR(file->f_inode->i_rdev);
	unsigned int dev_type;
	dev_dbg(g_dev_ptr, "minor = 0x%x\n", minor);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
206

207 208 209 210 211 212 213 214 215 216 217
	minor_to_chn(minor, &dev_type);
	switch (dev_type) {
	case CIRCBUF_MINOR:
		return circbuf_read(file, buf, count, off);
	case JPEGHEAD_MINOR:
		return jpeghead_read(file, buf, count, off);
	case HUFFMAN_MINOR:
		return huffman_read(file, buf, count, off);
	default:
		return -EINVAL;
	}
Mikhail Karpenko's avatar
Mikhail Karpenko committed
218 219
}

220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
ssize_t circbuf_all_write(struct file *file, const char *buf, size_t count, loff_t *off)
{
	unsigned int minor = MINOR(file->f_inode->i_rdev);
	unsigned int dev_type;
	dev_dbg(g_dev_ptr, "minor = 0x%x, count = %d, off = %d\n", minor, (int)count, (int)*off);

	minor_to_chn(minor, &dev_type);
	switch (dev_type) {
	case CIRCBUF_MINOR:
		return circbuf_write (file, buf, count, off);
	case JPEGHEAD_MINOR:
		// no method for this module
		return -EINVAL;
	case HUFFMAN_MINOR:
		return huffman_write (file, buf, count, off);
	default:
		return -EINVAL;
	}
Mikhail Karpenko's avatar
Mikhail Karpenko committed
238 239
}

240 241 242 243 244
int circbuf_all_mmap(struct file *file, struct vm_area_struct *vma)
{
	unsigned int minor = MINOR(file->f_inode->i_rdev);
	unsigned int dev_type;
	dev_dbg(g_dev_ptr, "minor = 0x%x\n", minor);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
245

246 247 248 249 250 251 252 253
	minor_to_chn(minor, &dev_type);
	switch (dev_type) {
	case CIRCBUF_MINOR:
		return circbuf_mmap(file, vma);
	default:
		return -EINVAL;
	}
}
Mikhail Karpenko's avatar
Mikhail Karpenko committed
254

255 256 257 258 259
unsigned int circbuf_all_poll (struct file *file, poll_table *wait)
{
	unsigned int minor = MINOR(file->f_inode->i_rdev);
	unsigned int dev_type;
	dev_dbg(g_dev_ptr, "minor = 0x%x\n", minor);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
260

261 262 263 264 265 266 267 268
	minor_to_chn(minor, &dev_type);
	switch (dev_type) {
	case CIRCBUF_MINOR:
		return circbuf_poll(file, wait);
	default:
		return -EINVAL;
	}
}
Mikhail Karpenko's avatar
Mikhail Karpenko committed
269

270 271 272
int circbuf_open(struct inode *inode, struct file *filp)
{
	inode->i_size = CCAM_DMA_SIZE;
273
	dev_dbg(g_dev_ptr, "inode->i_size = 0x%lx\n", inode->i_size);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
274

275
	return 0;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
276 277
}

278
void dump_interframe_params(struct interframe_params_t *params, int offset, unsigned int chn)
279
{
280 281
	dev_dbg(g_dev_ptr, "Dump of interframe parameters at offset 0x%x [chn %u]:\n", offset, chn);
	print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, params, sizeof(struct interframe_params_t) - 4);
282 283
}

284
/**
285
 * @brief Get the length of an image before given pointer
286
 */
287
unsigned long get_image_length(int byte_offset, unsigned int chn, int *last_chunk_offset)
288
{
289
	unsigned int offset;
290
	unsigned long len32;
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
	int last_image_chunk = X393_BUFFSUB(byte_offset, OFFSET_X40);

	offset = X393_BUFFADD(last_image_chunk, CHUNK_SIZE - CCAM_MMAP_META_LENGTH);
	len32 = circbuf_priv[chn].buf_ptr[BYTE2DW(offset)];

	dev_dbg(g_dev_ptr, "[chn %d] byte_offset = 0x%x, calculated offset = 0x%x, len32 = 0x%lx\n", chn, byte_offset, offset, len32);

	if ((len32 & MARKER_FF) != MARKER_FF) {
		dev_dbg(g_dev_ptr, "[chn %u] failed to get 0xff marker at offset 0x%x\n", chn, offset);
//		byte_offset = X393_BUFFSUB(byte_offset, CHUNK_SIZE);
		last_image_chunk = X393_BUFFSUB(byte_offset, OFFSET_X40 + CHUNK_SIZE);
		offset = X393_BUFFADD(last_image_chunk, CHUNK_SIZE - CCAM_MMAP_META_LENGTH);
		len32 = circbuf_priv[chn].buf_ptr[BYTE2DW(offset)];
		if ((len32 & MARKER_FF) != MARKER_FF) {
			dev_dbg(g_dev_ptr, "[chn %u] failed to get 0xff marker at CORRECTED offset 0x%x\n", chn, offset);
			return 0;
		}
	}
309

310
	dev_dbg(g_dev_ptr, "[chn %u] got len32 = 0x%lx at 0x%x\n", chn, len32, offset);
311

312
	if (last_chunk_offset != NULL)
313
		*last_chunk_offset = last_image_chunk;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
314

315 316 317
	return len32;
}

318 319 320 321 322 323 324
/**
 * @brief Check that read pointer is valid
 * @param[in]   rp   read pointer to be checked; this pointer is in bytes
 * @param[out]  fpp  pointer to #interframe_params_t structure, this pointer will be set to
 * frame header before \e rp and will point to its parameters
 * @param[in]   chn  specify compressor channel number which pointer should be checked
 * @return 0 if the pointer is for the frame yet to be acquired, 1 if there is a valid frame at this address,
325
 * 2 if file pointer should be advanced by 32 bytes,
326 327 328
 * -1 if there is no frame at this index, -2 if the pointer is not 32-bytes aligned
 * sets *fpp to the frame header, including signature and length
 */
329
int circbuf_valid_ptr(loff_t *rp_offset, struct interframe_params_t **fpp, unsigned int chn)
330
{
331
	int rp = *rp_offset;
332 333 334 335
	int last_image_chunk;
	unsigned int sec;
	unsigned int usec;
	int wp = camseq_get_jpeg_wp(chn);
336
	unsigned int len32 = get_image_length(DW2BYTE(wp), chn, &last_image_chunk);
337
	struct interframe_params_t *fp, *fp_off;
338

339 340
	if (rp & 0x1f) {
		// rp is not 32-bytes aligned
341
		dev_dbg(g_dev_ptr, "misaligned pointer rp = 0x%x for channel %d\n", rp, chn);
342 343
		return -2;
	}
344
	fp = (struct interframe_params_t *) &circbuf_priv[chn].buf_ptr[BYTE2DW(X393_BUFFSUB(rp, sizeof(struct interframe_params_t) - 4))];
345 346
	*fpp = fp;

347
	dump_interframe_params(fp, X393_BUFFSUB(rp, sizeof(struct interframe_params_t) - 4), chn);
348 349

	if (BYTE2DW(rp) == wp)
350
		// read pointer and write pointer coincide, frame not yet acquired
351 352
		return 0;

353
	if (fp->signffff != MARKER_FFFF) {
354 355 356 357 358 359 360 361 362 363 364 365
		dev_dbg(g_dev_ptr, "[chn %u] interframe signature is overwritten, signffff = 0x%x\n", chn, fp->signffff);
		fp_off = (struct interframe_params_t *) &circbuf_priv[chn].buf_ptr[BYTE2DW(rp)];
		if (fp_off->signffff != MARKER_FFFF) {
			dev_dbg(g_dev_ptr, "[chn %u] interframe signature is overwritten at CORRECTED offset, signffff = 0x%x\n", chn, fp_off->signffff);
			return -1;
		} else {
			dev_dbg(g_dev_ptr, "[chn %u] interframe pointer and file ponter is advanced by 0x20\n", chn);
			*fpp = fp_off;
			*rp_offset = X393_BUFFADD(*rp_offset, CHUNK_SIZE);
			dump_interframe_params(fp_off, rp, chn);
			return 2;
		}
366 367 368
	}

	return 1;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
369 370
}

371 372 373 374 375 376 377 378 379 380 381
/**
 * @brief Get image start offset pointed by its last data chunk
 * @param[in]   last_chunk_offset   offset of the last image data chunk
 * @param[in]   len32               length of image
 * @return      image start offset
 */
inline int get_image_start(int last_chunk_offset, unsigned int len32)
{
	return X393_BUFFSUB(last_chunk_offset + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
}

382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
/**
 * @brief Reposition read/write file offset
 *
 * This function is overloaded with additional functionality in order to avoid ioctls.
 * In case user-space program set <em>orig == SEEK_END</em>, \e lseek will treat (offset > 0) as a command
 * to manipulate frame pointer(s) or wait for the image to be ready using these commands:
 *
 *  LSEEK_CIRC_TORP  - set file pointer to global (shared) read pointer;
 *  LSEEK_CIRC_TOWP  - set file pointer to FPGA write pointer (next frame to be acquired);
 *  LSEEK_CIRC_PREV  - move pointer to the previous frame, return \e -EOVERFLOW if there are none;
 *  LSEEK_CIRC_NEXT  - advance pointer to the next frame, return \e -EOVERFLOW if it was at the last frame
 *  LSEEK_CIRC_LAST  - move pointer to the last acquired frame (default after open), this is a combination
 *                     of LSEEK_CIRC_TOWP and LSEEK_CIRC_PREV;
 *  LSEEK_CIRC_FIRST - move pointer to the first acquired frame. It s not safe to rely
 *                     on this pointer if more frames are expected - next incoming frame
 *                     can overwrite this one;
 *  LSEEK_CIRC_SCND -  move pointer to the second oldest acquired frame. A slightly safer
 *                     to use instead of LSEEK_CIRC_FIRST when constant acquisition is on
 *                     and sensor provides new frames - this frame will likely survive longer;
 *  LSEEK_CIRC_SETP -  save current pointer to global read pointer
 *  LSEEK_CIRC_VALID - verify that the frame at current location is valid (not overrun in the buffer).
 *                     Returns file pointer if it is valid and -1 otherwise;
 *  LSEEK_CIRC_READY - verify frame at current location is available (valid and acquired).
 *                     Returns file pointer if it is ready or -1 otherwise
 *  LSEEK_CIRC_WAIT -  sleep until next frame is acquired.
 * All commands but (LSEEK_CIRC_TOWP, LSEEK_CIRC_LAST, LSEEK_CIRC_FIRST) will return -EINVAL if read
 * pointer is not valid (i.e buffer was overrun and data pointed is lost). In case of success, they return
 * current (byte *) to the start of the frame data (parameters are at offset - 32 from it).
 * (0, SEEK_CUR) also verifies that the header is not overwritten. It can be used after buffering frame data to
 * verify you got it all correctly.
 *
 * SEEK_CUR also supports the circular nature of the buffer and rolls over if needed.
 *
 * Additional commands for SEEK_END (they _DO_ modify the current file pointer):
 *  LSEEK_CIRC_FREE -  returns remaining memory in circbuf from the current file pointer,
 *                   or -EINVAL if the pointer is invalid. As this command uses the buffer write pointer
 *                   that is updated only when the complete frame is in the buffer, the actual
 *                   free memory may be less by a whole frame if compressor is running.
 *  LSEEK_CIRC_USED - returns memory used in the in circbuf from the current file pointer,
 *                   or -EINVAL if the pointer is invalid
422 423
 * The following command is used for profiling from user space applications. It does not change file pointer:
 *  LSEEK_CIRC_UTIME  return current value of microsecond counter.
424 425 426 427 428
 *  @param[in]   file   pointer to \e file structure
 *  @param[in]   offset offset inside buffer in bytes
 *  @param[in]   orig   origin
 *  @return      current file pointer position if operation was successful and error code otherwise
 */
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
/* debug code follows */
void stop_compressor(unsigned int chn)
{
	x393_cmprs_mode_t mode;

	mode.run = 1;
	mode.run_set = 1;
	x393_cmprs_control_reg(mode, chn);
}
void dump_state(unsigned int chn)
{
	int img_start, last_image_chunk;
	int len32;
	int prev_ptr, prevprev_ptr;
	int read_ptr = DW2BYTE(camseq_get_jpeg_wp(chn));
	unsigned int nf, nz;
	struct interframe_params_t *fp;

	nf = 0;
	nz = 1;
	printk(KERN_DEBUG "=== start of state dump, chn = %d ===\n", chn);
	printk(KERN_DEBUG "hardware pointer at 0x%x\n", read_ptr);
	// move to the beginning of last frame
	len32 = get_image_length(read_ptr, chn, &last_image_chunk);
	if ((len32 & MARKER_FF) != MARKER_FF) {
		printk(KERN_DEBUG "last acquired frame at location 0x%x is damaged\n", read_ptr);
		return;
	}
	len32 &= FRAME_LENGTH_MASK;
	//img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
	img_start = get_image_start(last_image_chunk, len32);
	read_ptr = img_start;
	// move back in history
	while ((circbuf_valid_ptr(&read_ptr, &fp, chn) >= 0) && (nz >= 0)) {
		printk(KERN_DEBUG "analyzing frame starting at 0x%x\n", read_ptr);
		//printk(KERN_DEBUG "mem dump of 0x40 bytes at (pointer - 0x20) = 0x%x:\n", read_ptr - OFFSET_X40 / 2);
		//print_hex_dump_bytes("\t\t", DUMP_PREFIX_OFFSET, &circbuf_priv[chn].buf_ptr[BYTE2DW(read_ptr - OFFSET_X40 / 2)], OFFSET_X40);
		nf++;
		prevprev_ptr = prev_ptr;
		prev_ptr = read_ptr;
		len32 = get_image_length(read_ptr, chn, &last_image_chunk);
		if ((len32 & MARKER_FF) != MARKER_FF) {
			printk(KERN_DEBUG "\t\tno image before 0x%x\n", read_ptr);
			break;
		}
		len32 &= FRAME_LENGTH_MASK;
		//printk(KERN_DEBUG "\t\tgot len32 = 0x%x", len32);
		//img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
		img_start = get_image_start(last_image_chunk, len32);
		read_ptr = img_start;
		if (read_ptr > prev_ptr)
			nz--;
	}
	printk(KERN_DEBUG "=== end of state dump, %d frame(s) analyzed ===\n", nf);
}
/* end of debug code */
485 486 487
loff_t circbuf_lseek(struct file *file, loff_t offset, int orig)
{
	unsigned int len32 = 0;
488
	int last_image_chunk;
489
	int img_start, next_img, padded_frame;
490
	unsigned int minor = MINOR(file->f_inode->i_rdev);
491 492 493 494
	unsigned int chn = minor_to_chn(minor, NULL);
	struct interframe_params_t * fp;
	int fvld = -1;
	int rp, bp;
495
	dev_dbg(g_dev_ptr, "start processing LSEEK operation: offset = 0x%x, orig = 0x%x, file->f_pos = 0x%llx\n",(int) offset, (int) orig, file->f_pos);
496 497 498 499 500 501 502

	switch (orig) {
	case SEEK_SET:
		file->f_pos = offset;
		break;
	case SEEK_CUR:
		if (offset) file->f_pos += offset;
503
		else if (circbuf_valid_ptr(&file->f_pos, &fp, chn) < 0 ) return -EINVAL; //!no frames at the specified location or pointer is not 32-byte aligned
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
		break;
	case SEEK_END:
		if (offset <= 0) {
			file->f_pos = CCAM_DMA_SIZE + offset;
		} else {
			// verify current frame pointer
			switch (offset) {
			case LSEEK_CIRC_TORP:
				file->f_pos = camseq_get_jpeg_rp(chn) << 2;
			case LSEEK_CIRC_PREV:
			case LSEEK_CIRC_NEXT:
			case LSEEK_CIRC_SETP:
			case LSEEK_CIRC_VALID:
			case LSEEK_CIRC_READY:
			case LSEEK_CIRC_FREE:
			case LSEEK_CIRC_USED:
520
				if ((fvld = circbuf_valid_ptr(&file->f_pos, &fp, chn)) < 0)
521
					return -EINVAL; // no frames at the specified location
522 523 524 525 526 527 528 529 530 531 532 533
				break;
			/* debug code follows */
			case LSEEK_CIRC_STOP_COMPRESSOR:
			{
				int s;
				dev_dbg(g_dev_ptr, "stopping all compressors, current channel %d, fvld = %d, file->f_pos = 0x%llx\n", chn, fvld, file->f_pos);
				for (s = 0; s < SENSOR_PORTS; s++)
					stop_compressor(s);
				dump_state(chn);
			}
				break;
			/* end of debug code */
534 535 536
			}
			switch (offset) {
			case LSEEK_CIRC_FREE:
537
				dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_FREE: checking remaining memory in circbuf\n", chn);
538 539 540
				bp = file->f_pos - (camseq_get_jpeg_wp(chn) << 2);
				return (file->f_pos = (bp > 0) ? bp : (bp + CCAM_DMA_SIZE)); //!Has a side effect of moving a file pointer!
			case LSEEK_CIRC_USED:
541
				dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_USED: checking used memory in circbuf\n", chn);
542 543 544 545 546 547 548 549 550 551 552
				bp = (camseq_get_jpeg_wp(chn) << 2) - file->f_pos;
				return (file->f_pos = (bp > 0) ? bp : (bp + CCAM_DMA_SIZE)); //!Has a side effect of moving a file pointer!
			case LSEEK_CIRC_TORP:
				// no actions to be done here, the pointer was set on previous step
				break;
			case LSEEK_CIRC_TOWP:
				file->f_pos = camseq_get_jpeg_wp(chn) << 2;
				break;
			case LSEEK_CIRC_LAST:
				next_img = camseq_get_jpeg_wp(chn) << 2;

553 554
				dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_LAST: next_img = 0x%x, fvld = %d\n", chn, next_img, fvld);
				dev_dbg(g_dev_ptr, "[chn %u] mem dump of last 0x40 bytes in buffer\n", chn);
555 556 557 558 559
				print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, &circbuf_priv[chn].buf_ptr[BYTE2DW(next_img - OFFSET_X40)], OFFSET_X40);

				len32 = get_image_length(next_img, chn, &last_image_chunk);
				if ((len32 & MARKER_FF) != MARKER_FF) {
					// we should not be here as the position was checked in circbufValidPointer
560
					dev_dbg(g_dev_ptr, "[chn %u] failed to get marker 0xFF at 0x%x, len32: 0x%x\n", chn, next_img, len32);
561 562 563
					return -EOVERFLOW;
				}
				len32 &= FRAME_LENGTH_MASK;
564 565 566 567
				//img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
				img_start = get_image_start(last_image_chunk, len32);
				dev_dbg(g_dev_ptr, "[chn %u] calculated start address = 0x%x, length = 0x%x\n", chn, img_start, len32);
				if (circbuf_valid_ptr(&img_start, &fp, chn) < 0)
568 569
					return -EOVERFLOW;
				file->f_pos = img_start;
570
				dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_LAST: moving file->f_pos to 0x%llx\n", chn, file->f_pos);
571 572
				break;
			case LSEEK_CIRC_PREV:
573
				rp = file->f_pos;
574
				fvld = circbuf_valid_ptr(&rp, &fp, chn);
575

576 577
				dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_PREV: rp = 0x%x, fvld = %d\n", chn, rp, fvld);
				dev_dbg(g_dev_ptr, "[chn %u] mem dump of last 0x40 bytes in buffer number %d\n", chn, chn);
578
				print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, &circbuf_priv[chn].buf_ptr[BYTE2DW(rp - OFFSET_X40)], OFFSET_X40);
579

580
				len32 = get_image_length(rp, chn, &last_image_chunk);
581 582
				if ((len32 & MARKER_FF) != MARKER_FF) {
					// we should not be here as the position was checked in circbufValidPointer
583
					dev_dbg(g_dev_ptr, "[chn %u] failed to get marker 0xFF at 0x%x, len32: 0x%x\n", chn, rp, len32);
584 585
					return -EOVERFLOW;
				}
586
				len32 &= FRAME_LENGTH_MASK;
587 588 589
				//img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
				img_start = get_image_start(last_image_chunk, len32);
				dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_PREV: calculated start address = 0x%x, length = 0x%x\n", chn, img_start, len32);
590 591 592

				// move file pointer only if previous frame valid
				len32 = get_image_length(img_start, chn, NULL);
593
				if (circbuf_valid_ptr(&img_start, &fp, chn) < 0)
594 595 596 597
					return -EOVERFLOW;
				file->f_pos = img_start;
				break;
			case LSEEK_CIRC_NEXT:
598 599 600
				dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_NEXT: file->f_pos = 0x%llx, fvld = %d, fp->len32 = 0x%lx\n", chn,
						file->f_pos, fvld, fp->frame_length);
				if (fvld <= 0) {
601
					return -EOVERFLOW; //! no frames after current
602 603 604 605
				} else if (fvld == 2) {
					//file->f_pos += CHUNK_SIZE;
					dev_dbg(g_dev_ptr, "[chn %u] read pointer file->f_pos was advanced by 0x20 bytes\n", chn);
				}
606 607 608
				// calculate the full length of current frame and advance file pointer by this value
				padded_frame = fp->frame_length + INSERTED_BYTES(fp->frame_length) + CHUNK_SIZE + CCAM_MMAP_META;
				file->f_pos = X393_BUFFADD(file->f_pos, padded_frame); // do it even if the next frame does not yet exist
609
				dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_NEXT: padded_frame = %d; moving file->f_pos to 0x%llx\n", chn, padded_frame, file->f_pos);
610 611 612 613 614 615
				break;
			case LSEEK_CIRC_FIRST:
			case LSEEK_CIRC_SCND:
			{
				int nf = 0; // number of frames;
				int nz = 1; // number of start crossing of the circular buffer (counter to prevent looping forever)
616
				int rp_b;
617 618 619
				int prev_p, preprev_p;
				// starting from the write pointer to be able to count all the frames in the buffer
				rp = camseq_get_jpeg_wp(chn);
620
				prev_p = preprev_p = DW2BYTE(rp);
621
				file->f_pos = DW2BYTE(rp);
622 623
				rp_b = DW2BYTE(rp);
				while (((fvld = circbuf_valid_ptr(&rp_b, &fp, chn)) >= 0) & (nz >= 0)) {
624 625
					nf++;
					preprev_p = prev_p; // second known good (at least first one)
626 627 628 629
					prev_p=rp_b;          // now - current, known good
					len32 = get_image_length(rp_b, chn, &last_image_chunk);
					dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_FIRST or LSEEK_CIRC_SCND: number of frames = %d, rp_b = 0x%x, fvld = %d, len32 = 0x%x", chn,
							nf, rp_b, fvld, len32);
630 631
					if ((len32 & MARKER_FF) != MARKER_FF ) break;  //! no frames before rp (==prev_p)
					//! move rp to the previous frame
632
					len32 &= FRAME_LENGTH_MASK;
633 634 635 636 637 638
					//img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
					img_start = get_image_start(last_image_chunk, len32);
					dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_FIRST or LSEEK_CIRC_SCND: calculated start address = 0x%x, length = 0x%x\n", chn,
							img_start, len32);
					rp_b = img_start;
					if (rp_b > prev_p) nz--; // rolled through zero - make sure we'll not stuck in this loop forever
639
				}
640 641
				dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_FIRST or LSEEK_CIRC_SCND: finish stepping back through frames, number of frames = %d, rp = 0x%x, fvld = %d, len32 = 0x%x",
						chn, nf, rp, fvld, len32);
642 643 644 645
				file->f_pos = ((offset == LSEEK_CIRC_SCND) ? preprev_p : prev_p) << 2;
				break;
			}
			case LSEEK_CIRC_SETP:
646
				dev_dbg(g_dev_ptr, "[chn %u] LSEK_CIRC_SETP: Setting jpeg_rp for channel %d to file->f_pos = 0x%llx\n", chn, chn, file->f_pos);
647 648 649 650
				camseq_set_jpeg_rp(chn, file->f_pos >> 2);
				break;
			case LSEEK_CIRC_VALID:
				// no actions to be done here, the pointer was checked on previous step
651
				dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_VALID: no action required, fvld = %d, file->f_pos = 0x%llx\n", chn, fvld, file->f_pos);
652 653
				break;
			case LSEEK_CIRC_READY:
654
				dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_READY: checking fvld, fvld = %d, file->f_pos = 0x%llx\n", chn, fvld, file->f_pos);
655
				if (fvld <= 0) return -EINVAL; // no frame is available better code?
656 657
				break;
			case LSEEK_CIRC_WAIT:
658 659
				dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_WAIT: fvld = %d, file->f_pos = 0x%llx\n", chn, fvld, file->f_pos);
				while (((fvld=circbuf_valid_ptr(&file->f_pos, &fp, chn)))==0) { // only while not ready, ready or BAD - return
660 661 662 663
					wait_event_interruptible(circbuf_wait_queue, (camseq_get_jpeg_wp(chn) << 2) != file->f_pos);
				}
				if (fvld < 0) return -ESPIPE;      // invalid seek - have better code?
				return file->f_pos ; // data already available, return file pointer
664 665 666
			case LSEEK_CIRC_UTIME:
				return get_rtc_usec();
				break;
667 668
			default:
				if ((offset & ~0x1f)==LSEEK_DAEMON_CIRCBUF) {
669
					wait_event_interruptible(circbuf_wait_queue, get_imageParamsThis(chn, P_DAEMON_EN) & (1<<(offset & 0x1f)));
670 671
				}
			}
672
			dev_dbg(g_dev_ptr, "[chn %u] finish SEEK_END processing; return file->f_pos = %lld\n", chn, file->f_pos);
673 674 675 676 677 678 679 680 681 682
			return ( file->f_pos ); // file position >= 0
		}
		break;
	default:
		return -EINVAL;
	}
	// roll-over position
	while (file->f_pos < 0) file->f_pos += CCAM_DMA_SIZE;
	while (file->f_pos > CCAM_DMA_SIZE) file->f_pos -= CCAM_DMA_SIZE;
	if ((orig !=SEEK_END) && (file->f_pos == CCAM_DMA_SIZE)) file->f_pos=0; // only for lseek(fd,0,SEEK_END) the result will be file size, others will roll to 0
683
	dev_dbg(g_dev_ptr, "[chn %u] return file->f_pos = %lld\n", chn, file->f_pos);
684
	return  file->f_pos ;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
685 686
}

687
/**
688 689
 * @brief This function handles write operations for circbuf files.
 * Note: never use \e file->f_pos in this function.
690 691 692 693 694 695
 * @param[in]   file  pointer to <em>struct file</em>
 * @param[in]   buf   pointer to buffer containing data
 * @param[in]   count number of bytes in buffer
 * @param[in]   off   offset
 * @return      number of bytes read form \e buf
 */
696 697 698 699
unsigned short circbuf_quality = 100;
unsigned short circbuf_height = 1936;
unsigned short circbuf_width = 2592;
unsigned char circbuf_byrshift = 3;
700 701
ssize_t circbuf_write(struct file *file, const char *buf, size_t count, loff_t *off)
{
702
	unsigned long p;
703 704
	unsigned int minor = MINOR(file->f_inode->i_rdev);
	unsigned int chn = minor_to_chn(minor, NULL);
705 706 707 708 709
	int i;
	int ret;
	long val;
	char *buf_copy = (char *)buf;
	dev_dbg(g_dev_ptr, "minor = 0x%x, count = 0x%x, off = 0x%llx", minor, count, off);
710

711 712 713 714 715 716 717 718
	/* debug code follows*/
	switch (buf[0] - 0x30) {
	case 0:
		camera_interrupts(0);
		break;
	case 1:
		camera_interrupts(1);
		break;
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
	case 3:
		/* update image quality */
		buf_copy[count - 1] = 0;
		ret = kstrtol(&buf_copy[2], 10, &val);
		dev_dbg(g_dev_ptr, "ret: %d, buf[2]: %s\n", ret, &buf_copy[2]);
		if (count > 2 &&  ret == 0) {
			if (val > 0 && val <= 100) {
				circbuf_quality = val;
				dev_dbg(g_dev_ptr, "set quality %d\n", circbuf_quality);
			}
		} else {
			dev_dbg(g_dev_ptr, "error, unable to process quality parameter\n");
		}
		break;
	case 4:
		for (i = 0; i < SENSOR_PORTS; i++)
			stop_compressor(i);
		dump_state(chn);
		break;
	case 5:
		/* print debug statistics */
		{
		int j, cntr;
		long long res;
		for (i = 0; i < SENSOR_PORTS; i++) {
			cntr = get_zero_counter(i);
			printk(KERN_DEBUG "channel = %d, hw pointer = 0x%x, zero_counter = %d, corrected_offset = %d, frame_counter = %lld\n",
					i, DW2BYTE(camseq_get_jpeg_wp(i)), cntr, get_corrected_offset(i), get_frame_counter(i));
			if (cntr != 0) {
				for (j = 0; j < cntr; j++) {
					res = get_frame_pos(i, j);
					printk(KERN_DEBUG "\t\tzero cross frame number: %lld\n", res);
				}
			}
			}
		}
		break;
	case 6:
		/* update frame size */
		{
		unsigned int w, h;
		int res = sscanf(&buf[2], "%u:%u", &w, &h);
		if (res == 2) {
			circbuf_width = w;
			circbuf_height = h;
			dev_dbg(g_dev_ptr, "set image size %u x %u\n", w, h);
		}
		}
		break;
	case 7:
		/* update Bayer shift */
		{
		unsigned char val;
		int res = sscanf(&buf[2], "%u", &val);
		if (res == 1) {
			circbuf_byrshift = val;
			dev_dbg(g_dev_ptr, "set new bayer shift: %u\n", val);
		}
		}
		break;
779
	}
780
	/* debug code end */
781 782

	p = *off;
783 784 785 786 787 788
	if (p >= CCAM_DMA_SIZE)
		p = CCAM_DMA_SIZE;
	if ((p + count) > CCAM_DMA_SIZE)
		count = CCAM_DMA_SIZE - p;
	if (count) {
		if (copy_from_user(&circbuf_priv[chn].buf_ptr[BYTE2DW(p)], buf, count))
789 790 791 792
			return -EFAULT;
		*off += count;
	}
	return count;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
793 794
}

795
/**
796 797
 * @brief This function handles read operations for circbuf files.
 * Note: never use \e file->f_pos in this function.
798 799 800 801 802 803
 * @param[in]   file  pointer to <em>struct file</em>
 * @param[in]   buf   pointer to buffer where data will be written to
 * @param[in]   count number of bytes written to \e buf
 * @param[in]   off   offset
 * @return      number of bytes written to \e buf
 */
804 805 806 807 808
ssize_t circbuf_read(struct file *file, char *buf, size_t count, loff_t *off)
{
	unsigned long p;
	unsigned int minor = MINOR(file->f_inode->i_rdev);
	unsigned int chn = minor_to_chn(minor, NULL);
809
	dev_dbg(g_dev_ptr, "minor = 0x%x, count = 0x%x, off = 0x%llx", minor, count, off);
810 811 812 813 814 815 816 817 818 819 820 821

	p = *off;
	if (p >= CCAM_DMA_SIZE)
		p = CCAM_DMA_SIZE;
	if ((p + count) > CCAM_DMA_SIZE)
		count = CCAM_DMA_SIZE - p;
	if (count) {
		if (copy_to_user(buf, &circbuf_priv[chn].buf_ptr[BYTE2DW(p)], count))
			return -EFAULT;
		*off+=count;
	}
	return count;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
822 823
}

824 825 826
int circbuf_mmap(struct file *file, struct vm_area_struct *vma)
{
	int ret;
827 828
	unsigned int minor = MINOR(file->f_inode->i_rdev);
	unsigned int chn = minor_to_chn(minor, NULL);
829

830 831 832 833 834
	dev_dbg(g_dev_ptr, "vm_start = 0x%lx\n", vma->vm_start);
	dev_dbg(g_dev_ptr, "vm_end = 0x%lx\n", vma->vm_end);
	dev_dbg(g_dev_ptr, "vm_pgoff = 0x%lx\n", vma->vm_pgoff);
	dev_dbg(g_dev_ptr, "vm_file = 0x%lx\n", (unsigned long)vma->vm_file);
	dev_dbg(g_dev_ptr, "ccam_dma_buf = 0x%lx\n", (unsigned long)circbuf_priv[chn].phys_addr);
835

836
	/* remap_pfn_range will mark the range VM_IO and VM_RESERVED */
837 838 839 840 841 842
	ret = remap_pfn_range(vma,
			vma->vm_start,
			circbuf_priv[chn].phys_addr >> PAGE_SHIFT,
			vma->vm_end - vma->vm_start,
			vma->vm_page_prot);

843 844 845 846 847 848 849 850 851 852 853
//	ret = dma_common_mmap(g_dev_ptr, vma,
//			circbuf_priv[chn].buf_ptr,
//			circbuf_priv[chn].phys_addr,
//			pElphel_buf->size * PAGE_SIZE);
//
//	ret = arm_dma_mmap(g_dev_ptr, vma,
//			circbuf_priv[chn].buf_ptr,
//			circbuf_priv[chn].phys_addr,
//			pElphel_buf->size * PAGE_SIZE,
//			NULL);

854
	dev_dbg(g_dev_ptr, "remap_pfn_range returned 0x%x\n", ret);
855 856 857
	if (ret) return -EAGAIN;

	return 0;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
858
}
859 860 861 862 863 864 865 866 867 868 869 870 871 872

/**
 * @brief This driver method is called when user-space program performs <em>poll, select</em> or
 * \e epoll system call.
 *
 * If the current read pointer is invalid, circbuf_poll returns POLLHUP
 * as no data will be ever available until file pointer is reset.
 * If it is valid, wait is setup and blocking condition occurs in case
 * current file pointer is equal to the FPGA write pointer.
 * @param[in]   file   pointer to <em>struct file</em> structure
 * @param[in]   wait   pointer to <em>struct poll_table</em> structure
 * return       POLLHUP if pointer is invalid, (POLLIN | POLLRDNORM) if frame is ready,
 *              0 in case nothing is ready.
 */
873 874 875 876
unsigned int circbuf_poll (struct file *file, poll_table *wait)
{
	int w_ptr;
	unsigned int minor = MINOR(file->f_inode->i_rdev);
877
	unsigned int chn = minor_to_chn(minor, NULL);
878
	struct interframe_params_t * fp;
879
	int rslt;
880
	dev_dbg(g_dev_ptr, "minor = 0x%x\n", minor);
881

882
	rslt = circbuf_valid_ptr(&file->f_pos, &fp, chn);
883 884
	if (rslt < 0) {
		// not a valid read pointer, probable buffer overrun
885
		dev_dbg(g_dev_ptr, "invalid pointer file->f_pos = 0x%llx\n", file->f_pos);
886 887 888
		return  POLLHUP ;
	} else if (rslt > 0) {
		return POLLIN | POLLRDNORM; //! there was frame already available
889 890
	} else {
		// pointer valid, no frame yet
891
		poll_wait(file, &circbuf_wait_queue, wait);
892 893
		// Frame might become available during call to poll_wait so nobody will wake us up,
		// let's see if there is still no frame.
894 895 896 897 898
		w_ptr = camseq_get_jpeg_wp(chn) << 2;
		if (w_ptr != file->f_pos)
			return POLLIN | POLLRDNORM; //! we are lucky - got it
	}
	return 0; // nothing ready
Mikhail Karpenko's avatar
Mikhail Karpenko committed
899 900
}

901 902 903 904 905 906 907 908 909 910 911 912
static struct file_operations circbuf_fops = {
		.owner          = THIS_MODULE,
		.llseek         = circbuf_all_lseek,
		.read           = circbuf_all_read,
		.write          = circbuf_all_write,
		//ioctl:    circbuf_all_ioctl,
		.open           = circbuf_all_open,
		.mmap           = circbuf_all_mmap,
		.poll           = circbuf_all_poll,
		.release        = circbuf_all_release
};

913 914 915 916 917
/**
 * @brief cirbuf driver probing function
 * @param[in]   pdev   pointer to \b platform_device structure
 * @return      0 on success or negative error code otherwise
 */
918 919
static int circbuf_all_init(struct platform_device *pdev)
{
Mikhail Karpenko's avatar
Mikhail Karpenko committed
920
   int res;
921 922 923 924 925 926 927 928
   struct device *dev = &pdev->dev;
   const struct of_device_id *match;

   /* sanity check */
   match = of_match_device(elphel393_circbuf_of_match, dev);
   if (!match)
	   return -EINVAL;

929
   dev_dbg(dev, "registering character device with name 'circbuf_operations'");
Mikhail Karpenko's avatar
Mikhail Karpenko committed
930 931
   res = register_chrdev(CIRCBUF_MAJOR, "circbuf_operations", &circbuf_fops);
   if(res < 0) {
932 933
	   dev_err(dev, "couldn't get a major number %d.\n", CIRCBUF_MAJOR);
	   return res;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
934
   }
935
   dev_info(dev, "registered MAJOR: %d\n", CIRCBUF_MAJOR);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
936

937 938 939 940 941
   res = jpeghead_init(pdev);
   if (res < 0) {
	   dev_err(dev, "unable to initialize jpeghead module\n");
	   return res;
   }
942 943 944 945 946
   res = image_acq_init(pdev);
   if (res < 0) {
	   dev_err(dev, "unable to initialize sensor_common module\n");
	   return res;
   }
947 948 949 950 951 952 953 954 955
   res = init_ccam_dma_buf_ptr(pdev);
   if (res < 0) {
	   dev_err(dev, "ERROR allocating coherent DMA buffer\n");
	   return -ENOMEM;
   }

   dev_dbg(dev, "initialize circbuf wait queue\n");
   init_waitqueue_head(&circbuf_wait_queue);
   dev_dbg(dev, "initialize Huffman tables with default data\n");
956

957 958
   g_dev_ptr = dev;

Mikhail Karpenko's avatar
Mikhail Karpenko committed
959 960 961
   return 0;
}

962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984
static int circbuf_remove(struct platform_device *pdev)
{
	unregister_chrdev(CIRCBUF_MAJOR, "circbuf_operations");

	return 0;
}

static const struct of_device_id elphel393_circbuf_of_match[] = {
		{ .compatible = "elphel,elphel393-circbuf-1.00" },
		{ /* end of list */ }
};
MODULE_DEVICE_TABLE(of, elphel393_circbuf_of_match);

static struct platform_driver elphel393_circbuf = {
		.probe          = circbuf_all_init,
		.remove         = circbuf_remove,
		.driver = {
				.name = CIRCBUF_DRIVER_NAME,
				.of_match_table = elphel393_circbuf_of_match,
		},
};

module_platform_driver(elphel393_circbuf);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
985

986
MODULE_LICENSE("GPL");
Mikhail Karpenko's avatar
Mikhail Karpenko committed
987 988
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>.");
MODULE_DESCRIPTION(CIRCBUF_DRIVER_NAME);