Contributors: 6
Author Tokens Token Proportion Commits Commit Proportion
Michael Tretter 14676 99.38% 20 80.00%
Chuhong Yuan 81 0.55% 1 4.00%
Mauro Carvalho Chehab 6 0.04% 1 4.00%
Christoph Hellwig 2 0.01% 1 4.00%
Hans Verkuil 1 0.01% 1 4.00%
Colin Ian King 1 0.01% 1 4.00%
Total 14767 25

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de>
 *
 * Allegro DVT video encoder driver
 */

#include <linux/bits.h>
#include <linux/firmware.h>
#include <linux/gcd.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/log2.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-dma-contig.h>
#include <media/videobuf2-v4l2.h>

#include "allegro-mail.h"
#include "nal-h264.h"

/*
 * Support up to 4k video streams. The hardware actually supports higher
 * resolutions, which are specified in PG252 June 6, 2018 (H.264/H.265 Video
 * Codec Unit v1.1) Chapter 3.
 */
#define ALLEGRO_WIDTH_MIN 128
#define ALLEGRO_WIDTH_DEFAULT 1920
#define ALLEGRO_WIDTH_MAX 3840
#define ALLEGRO_HEIGHT_MIN 64
#define ALLEGRO_HEIGHT_DEFAULT 1080
#define ALLEGRO_HEIGHT_MAX 2160

#define ALLEGRO_FRAMERATE_DEFAULT ((struct v4l2_fract) { 30, 1 })

#define ALLEGRO_GOP_SIZE_DEFAULT 25
#define ALLEGRO_GOP_SIZE_MAX 1000

/*
 * MCU Control Registers
 *
 * The Zynq UltraScale+ Devices Register Reference documents the registers
 * with an offset of 0x9000, which equals the size of the SRAM and one page
 * gap. The driver handles SRAM and registers separately and, therefore, is
 * oblivious of the offset.
 */
#define AL5_MCU_RESET                   0x0000
#define AL5_MCU_RESET_SOFT              BIT(0)
#define AL5_MCU_RESET_REGS              BIT(1)
#define AL5_MCU_RESET_MODE              0x0004
#define AL5_MCU_RESET_MODE_SLEEP        BIT(0)
#define AL5_MCU_RESET_MODE_HALT         BIT(1)
#define AL5_MCU_STA                     0x0008
#define AL5_MCU_STA_SLEEP               BIT(0)
#define AL5_MCU_WAKEUP                  0x000c

#define AL5_ICACHE_ADDR_OFFSET_MSB      0x0010
#define AL5_ICACHE_ADDR_OFFSET_LSB      0x0014
#define AL5_DCACHE_ADDR_OFFSET_MSB      0x0018
#define AL5_DCACHE_ADDR_OFFSET_LSB      0x001c

#define AL5_MCU_INTERRUPT               0x0100
#define AL5_ITC_CPU_IRQ_MSK             0x0104
#define AL5_ITC_CPU_IRQ_CLR             0x0108
#define AL5_ITC_CPU_IRQ_STA             0x010C
#define AL5_ITC_CPU_IRQ_STA_TRIGGERED   BIT(0)

#define AXI_ADDR_OFFSET_IP              0x0208

/*
 * The MCU accesses the system memory with a 2G offset compared to CPU
 * physical addresses.
 */
#define MCU_CACHE_OFFSET SZ_2G

/*
 * The driver needs to reserve some space at the beginning of capture buffers,
 * because it needs to write SPS/PPS NAL units. The encoder writes the actual
 * frame data after the offset.
 */
#define ENCODER_STREAM_OFFSET SZ_64

#define SIZE_MACROBLOCK 16

static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-2)");

struct allegro_buffer {
	void *vaddr;
	dma_addr_t paddr;
	size_t size;
	struct list_head head;
};

struct allegro_channel;

struct allegro_mbox {
	unsigned int head;
	unsigned int tail;
	unsigned int data;
	size_t size;
	/* protect mailbox from simultaneous accesses */
	struct mutex lock;
};

struct allegro_dev {
	struct v4l2_device v4l2_dev;
	struct video_device video_dev;
	struct v4l2_m2m_dev *m2m_dev;
	struct platform_device *plat_dev;

	/* mutex protecting vb2_queue structure */
	struct mutex lock;

	struct regmap *regmap;
	struct regmap *sram;

	struct allegro_buffer firmware;
	struct allegro_buffer suballocator;

	struct completion init_complete;

	/* The mailbox interface */
	struct allegro_mbox mbox_command;
	struct allegro_mbox mbox_status;

	/*
	 * The downstream driver limits the users to 64 users, thus I can use
	 * a bitfield for the user_ids that are in use. See also user_id in
	 * struct allegro_channel.
	 */
	unsigned long channel_user_ids;
	struct list_head channels;
};

static struct regmap_config allegro_regmap_config = {
	.name = "regmap",
	.reg_bits = 32,
	.val_bits = 32,
	.reg_stride = 4,
	.max_register = 0xfff,
	.cache_type = REGCACHE_NONE,
};

static struct regmap_config allegro_sram_config = {
	.name = "sram",
	.reg_bits = 32,
	.val_bits = 32,
	.reg_stride = 4,
	.max_register = 0x7fff,
	.cache_type = REGCACHE_NONE,
};

enum allegro_state {
	ALLEGRO_STATE_ENCODING,
	ALLEGRO_STATE_DRAIN,
	ALLEGRO_STATE_WAIT_FOR_BUFFER,
	ALLEGRO_STATE_STOPPED,
};

#define fh_to_channel(__fh) container_of(__fh, struct allegro_channel, fh)

struct allegro_channel {
	struct allegro_dev *dev;
	struct v4l2_fh fh;
	struct v4l2_ctrl_handler ctrl_handler;

	unsigned int width;
	unsigned int height;
	unsigned int stride;
	struct v4l2_fract framerate;

	enum v4l2_colorspace colorspace;
	enum v4l2_ycbcr_encoding ycbcr_enc;
	enum v4l2_quantization quantization;
	enum v4l2_xfer_func xfer_func;

	u32 pixelformat;
	unsigned int sizeimage_raw;
	unsigned int osequence;

	u32 codec;
	enum v4l2_mpeg_video_h264_profile profile;
	enum v4l2_mpeg_video_h264_level level;
	unsigned int sizeimage_encoded;
	unsigned int csequence;

	bool frame_rc_enable;
	unsigned int bitrate;
	unsigned int bitrate_peak;
	unsigned int cpb_size;
	unsigned int gop_size;

	struct v4l2_ctrl *mpeg_video_h264_profile;
	struct v4l2_ctrl *mpeg_video_h264_level;
	struct v4l2_ctrl *mpeg_video_h264_i_frame_qp;
	struct v4l2_ctrl *mpeg_video_h264_max_qp;
	struct v4l2_ctrl *mpeg_video_h264_min_qp;
	struct v4l2_ctrl *mpeg_video_h264_p_frame_qp;
	struct v4l2_ctrl *mpeg_video_h264_b_frame_qp;
	struct v4l2_ctrl *mpeg_video_frame_rc_enable;
	struct { /* video bitrate mode control cluster */
		struct v4l2_ctrl *mpeg_video_bitrate_mode;
		struct v4l2_ctrl *mpeg_video_bitrate;
		struct v4l2_ctrl *mpeg_video_bitrate_peak;
	};
	struct v4l2_ctrl *mpeg_video_cpb_size;
	struct v4l2_ctrl *mpeg_video_gop_size;

	/* user_id is used to identify the channel during CREATE_CHANNEL */
	/* not sure, what to set here and if this is actually required */
	int user_id;
	/* channel_id is set by the mcu and used by all later commands */
	int mcu_channel_id;

	struct list_head buffers_reference;
	struct list_head buffers_intermediate;

	struct list_head source_shadow_list;
	struct list_head stream_shadow_list;
	/* protect shadow lists of buffers passed to firmware */
	struct mutex shadow_list_lock;

	struct list_head list;
	struct completion completion;

	unsigned int error;
	enum allegro_state state;
};

static inline int
allegro_set_state(struct allegro_channel *channel, enum allegro_state state)
{
	channel->state = state;

	return 0;
}

static inline enum allegro_state
allegro_get_state(struct allegro_channel *channel)
{
	return channel->state;
}

struct allegro_m2m_buffer {
	struct v4l2_m2m_buffer buf;
	struct list_head head;
};

#define to_allegro_m2m_buffer(__buf) \
	container_of(__buf, struct allegro_m2m_buffer, buf)

struct fw_info {
	unsigned int id;
	unsigned int id_codec;
	char *version;
	unsigned int mailbox_cmd;
	unsigned int mailbox_status;
	size_t mailbox_size;
	size_t suballocator_size;
};

static const struct fw_info supported_firmware[] = {
	{
		.id = 18296,
		.id_codec = 96272,
		.version = "v2018.2",
		.mailbox_cmd = 0x7800,
		.mailbox_status = 0x7c00,
		.mailbox_size = 0x400 - 0x8,
		.suballocator_size = SZ_16M,
	},
};

static inline u32 to_mcu_addr(struct allegro_dev *dev, dma_addr_t phys)
{
	if (upper_32_bits(phys) || (lower_32_bits(phys) & MCU_CACHE_OFFSET))
		v4l2_warn(&dev->v4l2_dev,
			  "address %pad is outside mcu window\n", &phys);

	return lower_32_bits(phys) | MCU_CACHE_OFFSET;
}

static inline u32 to_mcu_size(struct allegro_dev *dev, size_t size)
{
	return lower_32_bits(size);
}

static inline u32 to_codec_addr(struct allegro_dev *dev, dma_addr_t phys)
{
	if (upper_32_bits(phys))
		v4l2_warn(&dev->v4l2_dev,
			  "address %pad cannot be used by codec\n", &phys);

	return lower_32_bits(phys);
}

static inline u64 ptr_to_u64(const void *ptr)
{
	return (uintptr_t)ptr;
}

/* Helper functions for channel and user operations */

static unsigned long allegro_next_user_id(struct allegro_dev *dev)
{
	if (dev->channel_user_ids == ~0UL)
		return -EBUSY;

	return ffz(dev->channel_user_ids);
}

static struct allegro_channel *
allegro_find_channel_by_user_id(struct allegro_dev *dev,
				unsigned int user_id)
{
	struct allegro_channel *channel;

	list_for_each_entry(channel, &dev->channels, list) {
		if (channel->user_id == user_id)
			return channel;
	}

	return ERR_PTR(-EINVAL);
}

static struct allegro_channel *
allegro_find_channel_by_channel_id(struct allegro_dev *dev,
				   unsigned int channel_id)
{
	struct allegro_channel *channel;

	list_for_each_entry(channel, &dev->channels, list) {
		if (channel->mcu_channel_id == channel_id)
			return channel;
	}

	return ERR_PTR(-EINVAL);
}

static inline bool channel_exists(struct allegro_channel *channel)
{
	return channel->mcu_channel_id != -1;
}

#define AL_ERROR			0x80
#define AL_ERR_INIT_FAILED		0x81
#define AL_ERR_NO_FRAME_DECODED		0x82
#define AL_ERR_RESOLUTION_CHANGE	0x85
#define AL_ERR_NO_MEMORY		0x87
#define AL_ERR_STREAM_OVERFLOW		0x88
#define AL_ERR_TOO_MANY_SLICES		0x89
#define AL_ERR_BUF_NOT_READY		0x8c
#define AL_ERR_NO_CHANNEL_AVAILABLE	0x8d
#define AL_ERR_RESOURCE_UNAVAILABLE	0x8e
#define AL_ERR_NOT_ENOUGH_CORES		0x8f
#define AL_ERR_REQUEST_MALFORMED	0x90
#define AL_ERR_CMD_NOT_ALLOWED		0x91
#define AL_ERR_INVALID_CMD_VALUE	0x92

static inline const char *allegro_err_to_string(unsigned int err)
{
	switch (err) {
	case AL_ERR_INIT_FAILED:
		return "initialization failed";
	case AL_ERR_NO_FRAME_DECODED:
		return "no frame decoded";
	case AL_ERR_RESOLUTION_CHANGE:
		return "resolution change";
	case AL_ERR_NO_MEMORY:
		return "out of memory";
	case AL_ERR_STREAM_OVERFLOW:
		return "stream buffer overflow";
	case AL_ERR_TOO_MANY_SLICES:
		return "too many slices";
	case AL_ERR_BUF_NOT_READY:
		return "buffer not ready";
	case AL_ERR_NO_CHANNEL_AVAILABLE:
		return "no channel available";
	case AL_ERR_RESOURCE_UNAVAILABLE:
		return "resource unavailable";
	case AL_ERR_NOT_ENOUGH_CORES:
		return "not enough cores";
	case AL_ERR_REQUEST_MALFORMED:
		return "request malformed";
	case AL_ERR_CMD_NOT_ALLOWED:
		return "command not allowed";
	case AL_ERR_INVALID_CMD_VALUE:
		return "invalid command value";
	case AL_ERROR:
	default:
		return "unknown error";
	}
}

static unsigned int estimate_stream_size(unsigned int width,
					 unsigned int height)
{
	unsigned int offset = ENCODER_STREAM_OFFSET;
	unsigned int num_blocks = DIV_ROUND_UP(width, SIZE_MACROBLOCK) *
					DIV_ROUND_UP(height, SIZE_MACROBLOCK);
	unsigned int pcm_size = SZ_256;
	unsigned int partition_table = SZ_256;

	return round_up(offset + num_blocks * pcm_size + partition_table, 32);
}

static enum v4l2_mpeg_video_h264_level
select_minimum_h264_level(unsigned int width, unsigned int height)
{
	unsigned int pic_width_in_mb = DIV_ROUND_UP(width, SIZE_MACROBLOCK);
	unsigned int frame_height_in_mb = DIV_ROUND_UP(height, SIZE_MACROBLOCK);
	unsigned int frame_size_in_mb = pic_width_in_mb * frame_height_in_mb;
	enum v4l2_mpeg_video_h264_level level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0;

	/*
	 * The level limits are specified in Rec. ITU-T H.264 Annex A.3.1 and
	 * also specify limits regarding bit rate and CBP size. Only approximate
	 * the levels using the frame size.
	 *
	 * Level 5.1 allows up to 4k video resolution.
	 */
	if (frame_size_in_mb <= 99)
		level = V4L2_MPEG_VIDEO_H264_LEVEL_1_0;
	else if (frame_size_in_mb <= 396)
		level = V4L2_MPEG_VIDEO_H264_LEVEL_1_1;
	else if (frame_size_in_mb <= 792)
		level = V4L2_MPEG_VIDEO_H264_LEVEL_2_1;
	else if (frame_size_in_mb <= 1620)
		level = V4L2_MPEG_VIDEO_H264_LEVEL_2_2;
	else if (frame_size_in_mb <= 3600)
		level = V4L2_MPEG_VIDEO_H264_LEVEL_3_1;
	else if (frame_size_in_mb <= 5120)
		level = V4L2_MPEG_VIDEO_H264_LEVEL_3_2;
	else if (frame_size_in_mb <= 8192)
		level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0;
	else if (frame_size_in_mb <= 8704)
		level = V4L2_MPEG_VIDEO_H264_LEVEL_4_2;
	else if (frame_size_in_mb <= 22080)
		level = V4L2_MPEG_VIDEO_H264_LEVEL_5_0;
	else
		level = V4L2_MPEG_VIDEO_H264_LEVEL_5_1;

	return level;
}

static unsigned int maximum_bitrate(enum v4l2_mpeg_video_h264_level level)
{
	switch (level) {
	case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
		return 64000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
		return 128000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
		return 192000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
		return 384000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
		return 768000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
		return 2000000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
		return 4000000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
		return 4000000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
		return 10000000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
		return 14000000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
		return 20000000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
		return 20000000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
		return 50000000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
		return 50000000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
		return 135000000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
	default:
		return 240000000;
	}
}

static unsigned int maximum_cpb_size(enum v4l2_mpeg_video_h264_level level)
{
	switch (level) {
	case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
		return 175;
	case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
		return 350;
	case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
		return 500;
	case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
		return 1000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
		return 2000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
		return 2000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
		return 4000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
		return 4000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
		return 10000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
		return 14000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
		return 20000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
		return 25000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
		return 62500;
	case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
		return 62500;
	case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
		return 135000;
	case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
	default:
		return 240000;
	}
}

static const struct fw_info *
allegro_get_firmware_info(struct allegro_dev *dev,
			  const struct firmware *fw,
			  const struct firmware *fw_codec)
{
	int i;
	unsigned int id = fw->size;
	unsigned int id_codec = fw_codec->size;

	for (i = 0; i < ARRAY_SIZE(supported_firmware); i++)
		if (supported_firmware[i].id == id &&
		    supported_firmware[i].id_codec == id_codec)
			return &supported_firmware[i];

	return NULL;
}

/*
 * Buffers that are used internally by the MCU.
 */

static int allegro_alloc_buffer(struct allegro_dev *dev,
				struct allegro_buffer *buffer, size_t size)
{
	buffer->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size,
					   &buffer->paddr, GFP_KERNEL);
	if (!buffer->vaddr)
		return -ENOMEM;
	buffer->size = size;

	return 0;
}

static void allegro_free_buffer(struct allegro_dev *dev,
				struct allegro_buffer *buffer)
{
	if (buffer->vaddr) {
		dma_free_coherent(&dev->plat_dev->dev, buffer->size,
				  buffer->vaddr, buffer->paddr);
		buffer->vaddr = NULL;
		buffer->size = 0;
	}
}

/*
 * Mailbox interface to send messages to the MCU.
 */

static int allegro_mbox_init(struct allegro_dev *dev,
			     struct allegro_mbox *mbox,
			     unsigned int base, size_t size)
{
	if (!mbox)
		return -EINVAL;

	mbox->head = base;
	mbox->tail = base + 0x4;
	mbox->data = base + 0x8;
	mbox->size = size;
	mutex_init(&mbox->lock);

	regmap_write(dev->sram, mbox->head, 0);
	regmap_write(dev->sram, mbox->tail, 0);

	return 0;
}

static int allegro_mbox_write(struct allegro_dev *dev,
			      struct allegro_mbox *mbox, void *src, size_t size)
{
	struct mcu_msg_header *header = src;
	unsigned int tail;
	size_t size_no_wrap;
	int err = 0;

	if (!src)
		return -EINVAL;

	if (size > mbox->size) {
		v4l2_err(&dev->v4l2_dev,
			 "message (%zu bytes) too large for mailbox (%zu bytes)\n",
			 size, mbox->size);
		return -EINVAL;
	}

	if (header->length != size - sizeof(*header)) {
		v4l2_err(&dev->v4l2_dev,
			 "invalid message length: %u bytes (expected %zu bytes)\n",
			 header->length, size - sizeof(*header));
		return -EINVAL;
	}

	v4l2_dbg(2, debug, &dev->v4l2_dev,
		 "write command message: type %s, body length %d\n",
		 msg_type_name(header->type), header->length);

	mutex_lock(&mbox->lock);
	regmap_read(dev->sram, mbox->tail, &tail);
	if (tail > mbox->size) {
		v4l2_err(&dev->v4l2_dev,
			 "invalid tail (0x%x): must be smaller than mailbox size (0x%zx)\n",
			 tail, mbox->size);
		err = -EIO;
		goto out;
	}
	size_no_wrap = min(size, mbox->size - (size_t)tail);
	regmap_bulk_write(dev->sram, mbox->data + tail, src, size_no_wrap / 4);
	regmap_bulk_write(dev->sram, mbox->data,
			  src + size_no_wrap, (size - size_no_wrap) / 4);
	regmap_write(dev->sram, mbox->tail, (tail + size) % mbox->size);

out:
	mutex_unlock(&mbox->lock);

	return err;
}

static ssize_t allegro_mbox_read(struct allegro_dev *dev,
				 struct allegro_mbox *mbox,
				 void *dst, size_t nbyte)
{
	struct mcu_msg_header *header;
	unsigned int head;
	ssize_t size;
	size_t body_no_wrap;

	regmap_read(dev->sram, mbox->head, &head);
	if (head > mbox->size) {
		v4l2_err(&dev->v4l2_dev,
			 "invalid head (0x%x): must be smaller than mailbox size (0x%zx)\n",
			 head, mbox->size);
		return -EIO;
	}

	/* Assume that the header does not wrap. */
	regmap_bulk_read(dev->sram, mbox->data + head,
			 dst, sizeof(*header) / 4);
	header = dst;
	size = header->length + sizeof(*header);
	if (size > mbox->size || size & 0x3) {
		v4l2_err(&dev->v4l2_dev,
			 "invalid message length: %zu bytes (maximum %zu bytes)\n",
			 header->length + sizeof(*header), mbox->size);
		return -EIO;
	}
	if (size > nbyte) {
		v4l2_err(&dev->v4l2_dev,
			 "destination buffer too small: %zu bytes (need %zu bytes)\n",
			 nbyte, size);
		return -EINVAL;
	}

	/*
	 * The message might wrap within the mailbox. If the message does not
	 * wrap, the first read will read the entire message, otherwise the
	 * first read will read message until the end of the mailbox and the
	 * second read will read the remaining bytes from the beginning of the
	 * mailbox.
	 *
	 * Skip the header, as was already read to get the size of the body.
	 */
	body_no_wrap = min((size_t)header->length,
			   (size_t)(mbox->size - (head + sizeof(*header))));
	regmap_bulk_read(dev->sram, mbox->data + head + sizeof(*header),
			 dst + sizeof(*header), body_no_wrap / 4);
	regmap_bulk_read(dev->sram, mbox->data,
			 dst + sizeof(*header) + body_no_wrap,
			 (header->length - body_no_wrap) / 4);

	regmap_write(dev->sram, mbox->head, (head + size) % mbox->size);

	v4l2_dbg(2, debug, &dev->v4l2_dev,
		 "read status message: type %s, body length %d\n",
		 msg_type_name(header->type), header->length);

	return size;
}

static void allegro_mcu_interrupt(struct allegro_dev *dev)
{
	regmap_write(dev->regmap, AL5_MCU_INTERRUPT, BIT(0));
}

static void allegro_mcu_send_init(struct allegro_dev *dev,
				  dma_addr_t suballoc_dma, size_t suballoc_size)
{
	struct mcu_msg_init_request msg;

	memset(&msg, 0, sizeof(msg));

	msg.header.type = MCU_MSG_TYPE_INIT;
	msg.header.length = sizeof(msg) - sizeof(msg.header);

	msg.suballoc_dma = to_mcu_addr(dev, suballoc_dma);
	msg.suballoc_size = to_mcu_size(dev, suballoc_size);

	/* disable L2 cache */
	msg.l2_cache[0] = -1;
	msg.l2_cache[1] = -1;
	msg.l2_cache[2] = -1;

	allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg));
	allegro_mcu_interrupt(dev);
}

static u32 v4l2_pixelformat_to_mcu_format(u32 pixelformat)
{
	switch (pixelformat) {
	case V4L2_PIX_FMT_NV12:
		/* AL_420_8BITS: 0x100 -> NV12, 0x88 -> 8 bit */
		return 0x100 | 0x88;
	default:
		return -EINVAL;
	}
}

static u32 v4l2_colorspace_to_mcu_colorspace(enum v4l2_colorspace colorspace)
{
	switch (colorspace) {
	case V4L2_COLORSPACE_REC709:
		return 2;
	case V4L2_COLORSPACE_SMPTE170M:
		return 3;
	case V4L2_COLORSPACE_SMPTE240M:
		return 4;
	case V4L2_COLORSPACE_SRGB:
		return 7;
	default:
		/* UNKNOWN */
		return 0;
	}
}

static s8 v4l2_pixelformat_to_mcu_codec(u32 pixelformat)
{
	switch (pixelformat) {
	case V4L2_PIX_FMT_H264:
	default:
		return 1;
	}
}

static u8 v4l2_profile_to_mcu_profile(enum v4l2_mpeg_video_h264_profile profile)
{
	switch (profile) {
	case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
	default:
		return 66;
	}
}

static u16 v4l2_level_to_mcu_level(enum v4l2_mpeg_video_h264_level level)
{
	switch (level) {
	case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
		return 10;
	case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
		return 11;
	case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
		return 12;
	case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
		return 13;
	case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
		return 20;
	case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
		return 21;
	case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
		return 22;
	case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
		return 30;
	case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
		return 31;
	case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
		return 32;
	case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
		return 40;
	case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
		return 41;
	case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
		return 42;
	case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
		return 50;
	case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
	default:
		return 51;
	}
}

static u32
v4l2_bitrate_mode_to_mcu_mode(enum v4l2_mpeg_video_bitrate_mode mode)
{
	switch (mode) {
	case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR:
		return 2;
	case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR:
	default:
		return 1;
	}
}

static u32 v4l2_cpb_size_to_mcu(unsigned int cpb_size, unsigned int bitrate)
{
	unsigned int cpb_size_kbit;
	unsigned int bitrate_kbps;

	/*
	 * The mcu expects the CPB size in units of a 90 kHz clock, but the
	 * channel follows the V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE and stores
	 * the CPB size in kilobytes.
	 */
	cpb_size_kbit = cpb_size * BITS_PER_BYTE;
	bitrate_kbps = bitrate / 1000;

	return (cpb_size_kbit * 90000) / bitrate_kbps;
}

static s16 get_qp_delta(int minuend, int subtrahend)
{
	if (minuend == subtrahend)
		return -1;
	else
		return minuend - subtrahend;
}

static int fill_create_channel_param(struct allegro_channel *channel,
				     struct create_channel_param *param)
{
	int i_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_i_frame_qp);
	int p_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_p_frame_qp);
	int b_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_b_frame_qp);
	int bitrate_mode = v4l2_ctrl_g_ctrl(channel->mpeg_video_bitrate_mode);

	param->width = channel->width;
	param->height = channel->height;
	param->format = v4l2_pixelformat_to_mcu_format(channel->pixelformat);
	param->colorspace =
		v4l2_colorspace_to_mcu_colorspace(channel->colorspace);
	param->src_mode = 0x0;
	param->profile = v4l2_profile_to_mcu_profile(channel->profile);
	param->constraint_set_flags = BIT(1);
	param->codec = v4l2_pixelformat_to_mcu_codec(channel->codec);
	param->level = v4l2_level_to_mcu_level(channel->level);
	param->tier = 0;
	param->sps_param = BIT(20) | 0x4a;
	param->pps_param = BIT(2);
	param->enc_option = AL_OPT_RDO_COST_MODE | AL_OPT_LF_X_TILE |
			    AL_OPT_LF_X_SLICE | AL_OPT_LF;
	param->beta_offset = -1;
	param->tc_offset = -1;
	param->num_slices = 1;
	param->me_range[0] = 8;
	param->me_range[1] = 8;
	param->me_range[2] = 16;
	param->me_range[3] = 16;
	param->max_cu_size = ilog2(SIZE_MACROBLOCK);
	param->min_cu_size = ilog2(8);
	param->max_tu_size = 2;
	param->min_tu_size = 2;
	param->max_transfo_depth_intra = 1;
	param->max_transfo_depth_inter = 1;

	param->prefetch_auto = 0;
	param->prefetch_mem_offset = 0;
	param->prefetch_mem_size = 0;

	param->rate_control_mode = channel->frame_rc_enable ?
		v4l2_bitrate_mode_to_mcu_mode(bitrate_mode) : 0;

	param->cpb_size = v4l2_cpb_size_to_mcu(channel->cpb_size,
					       channel->bitrate_peak);
	/* Shall be ]0;cpb_size in 90 kHz units]. Use maximum value. */
	param->initial_rem_delay = param->cpb_size;
	param->framerate = DIV_ROUND_UP(channel->framerate.numerator,
					channel->framerate.denominator);
	param->clk_ratio = channel->framerate.denominator == 1001 ? 1001 : 1000;
	param->target_bitrate = channel->bitrate;
	param->max_bitrate = channel->bitrate_peak;
	param->initial_qp = i_frame_qp;
	param->min_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_min_qp);
	param->max_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_max_qp);
	param->ip_delta = get_qp_delta(i_frame_qp, p_frame_qp);
	param->pb_delta = get_qp_delta(p_frame_qp, b_frame_qp);
	param->golden_ref = 0;
	param->golden_delta = 2;
	param->golden_ref_frequency = 10;
	param->rate_control_option = 0x00000000;

	param->gop_ctrl_mode = 0x00000000;
	param->freq_idr = channel->gop_size;
	param->freq_lt = 0;
	param->gdr_mode = 0x00000000;
	param->gop_length = channel->gop_size;
	param->subframe_latency = 0x00000000;

	return 0;
}

static int allegro_mcu_send_create_channel(struct allegro_dev *dev,
					   struct allegro_channel *channel)
{
	struct mcu_msg_create_channel msg;

	memset(&msg, 0, sizeof(msg));

	msg.header.type = MCU_MSG_TYPE_CREATE_CHANNEL;
	msg.header.length = sizeof(msg) - sizeof(msg.header);

	msg.user_id = channel->user_id;

	fill_create_channel_param(channel, &msg.param);

	allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg));
	allegro_mcu_interrupt(dev);

	return 0;
}

static int allegro_mcu_send_destroy_channel(struct allegro_dev *dev,
					    struct allegro_channel *channel)
{
	struct mcu_msg_destroy_channel msg;

	memset(&msg, 0, sizeof(msg));

	msg.header.type = MCU_MSG_TYPE_DESTROY_CHANNEL;
	msg.header.length = sizeof(msg) - sizeof(msg.header);

	msg.channel_id = channel->mcu_channel_id;

	allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg));
	allegro_mcu_interrupt(dev);

	return 0;
}

static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev,
					      struct allegro_channel *channel,
					      dma_addr_t paddr,
					      unsigned long size,
					      u64 stream_id)
{
	struct mcu_msg_put_stream_buffer msg;

	memset(&msg, 0, sizeof(msg));

	msg.header.type = MCU_MSG_TYPE_PUT_STREAM_BUFFER;
	msg.header.length = sizeof(msg) - sizeof(msg.header);

	msg.channel_id = channel->mcu_channel_id;
	msg.dma_addr = to_codec_addr(dev, paddr);
	msg.mcu_addr = to_mcu_addr(dev, paddr);
	msg.size = size;
	msg.offset = ENCODER_STREAM_OFFSET;
	/* copied to mcu_msg_encode_frame_response */
	msg.stream_id = stream_id;

	allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg));
	allegro_mcu_interrupt(dev);

	return 0;
}

static int allegro_mcu_send_encode_frame(struct allegro_dev *dev,
					 struct allegro_channel *channel,
					 dma_addr_t src_y, dma_addr_t src_uv,
					 u64 src_handle)
{
	struct mcu_msg_encode_frame msg;

	memset(&msg, 0, sizeof(msg));

	msg.header.type = MCU_MSG_TYPE_ENCODE_FRAME;
	msg.header.length = sizeof(msg) - sizeof(msg.header);

	msg.channel_id = channel->mcu_channel_id;
	msg.encoding_options = AL_OPT_FORCE_LOAD;
	msg.pps_qp = 26; /* qp are relative to 26 */
	msg.user_param = 0; /* copied to mcu_msg_encode_frame_response */
	/* src_handle is copied to mcu_msg_encode_frame_response */
	msg.src_handle = src_handle;
	msg.src_y = to_codec_addr(dev, src_y);
	msg.src_uv = to_codec_addr(dev, src_uv);
	msg.stride = channel->stride;
	msg.ep2 = 0x0;
	msg.ep2_v = to_mcu_addr(dev, msg.ep2);

	allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg));
	allegro_mcu_interrupt(dev);

	return 0;
}

static int allegro_mcu_wait_for_init_timeout(struct allegro_dev *dev,
					     unsigned long timeout_ms)
{
	unsigned long tmo;

	tmo = wait_for_completion_timeout(&dev->init_complete,
					  msecs_to_jiffies(timeout_ms));
	if (tmo == 0)
		return -ETIMEDOUT;

	reinit_completion(&dev->init_complete);
	return 0;
}

static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel,
					    enum mcu_msg_type type)
{
	struct allegro_dev *dev = channel->dev;
	struct mcu_msg_push_buffers_internal *msg;
	struct mcu_msg_push_buffers_internal_buffer *buffer;
	unsigned int num_buffers = 0;
	size_t size;
	struct allegro_buffer *al_buffer;
	struct list_head *list;
	int err;

	switch (type) {
	case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE:
		list = &channel->buffers_reference;
		break;
	case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE:
		list = &channel->buffers_intermediate;
		break;
	default:
		return -EINVAL;
	}

	list_for_each_entry(al_buffer, list, head)
		num_buffers++;
	size = struct_size(msg, buffer, num_buffers);

	msg = kmalloc(size, GFP_KERNEL);
	if (!msg)
		return -ENOMEM;

	msg->header.length = size - sizeof(msg->header);
	msg->header.type = type;
	msg->channel_id = channel->mcu_channel_id;

	buffer = msg->buffer;
	list_for_each_entry(al_buffer, list, head) {
		buffer->dma_addr = to_codec_addr(dev, al_buffer->paddr);
		buffer->mcu_addr = to_mcu_addr(dev, al_buffer->paddr);
		buffer->size = to_mcu_size(dev, al_buffer->size);
		buffer++;
	}

	err = allegro_mbox_write(dev, &dev->mbox_command, msg, size);
	if (err)
		goto out;
	allegro_mcu_interrupt(dev);

out:
	kfree(msg);
	return err;
}

static int allegro_mcu_push_buffer_intermediate(struct allegro_channel *channel)
{
	enum mcu_msg_type type = MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE;

	return allegro_mcu_push_buffer_internal(channel, type);
}

static int allegro_mcu_push_buffer_reference(struct allegro_channel *channel)
{
	enum mcu_msg_type type = MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE;

	return allegro_mcu_push_buffer_internal(channel, type);
}

static int allocate_buffers_internal(struct allegro_channel *channel,
				     struct list_head *list,
				     size_t n, size_t size)
{
	struct allegro_dev *dev = channel->dev;
	unsigned int i;
	int err;
	struct allegro_buffer *buffer, *tmp;

	for (i = 0; i < n; i++) {
		buffer = kmalloc(sizeof(*buffer), GFP_KERNEL);
		if (!buffer) {
			err = -ENOMEM;
			goto err;
		}
		INIT_LIST_HEAD(&buffer->head);

		err = allegro_alloc_buffer(dev, buffer, size);
		if (err)
			goto err;
		list_add(&buffer->head, list);
	}

	return 0;

err:
	list_for_each_entry_safe(buffer, tmp, list, head) {
		list_del(&buffer->head);
		allegro_free_buffer(dev, buffer);
		kfree(buffer);
	}
	return err;
}

static void destroy_buffers_internal(struct allegro_channel *channel,
				     struct list_head *list)
{
	struct allegro_dev *dev = channel->dev;
	struct allegro_buffer *buffer, *tmp;

	list_for_each_entry_safe(buffer, tmp, list, head) {
		list_del(&buffer->head);
		allegro_free_buffer(dev, buffer);
		kfree(buffer);
	}
}

static void destroy_reference_buffers(struct allegro_channel *channel)
{
	return destroy_buffers_internal(channel, &channel->buffers_reference);
}

static void destroy_intermediate_buffers(struct allegro_channel *channel)
{
	return destroy_buffers_internal(channel,
					&channel->buffers_intermediate);
}

static int allocate_intermediate_buffers(struct allegro_channel *channel,
					 size_t n, size_t size)
{
	return allocate_buffers_internal(channel,
					 &channel->buffers_intermediate,
					 n, size);
}

static int allocate_reference_buffers(struct allegro_channel *channel,
				      size_t n, size_t size)
{
	return allocate_buffers_internal(channel,
					 &channel->buffers_reference,
					 n, PAGE_ALIGN(size));
}

static ssize_t allegro_h264_write_sps(struct allegro_channel *channel,
				      void *dest, size_t n)
{
	struct allegro_dev *dev = channel->dev;
	struct nal_h264_sps *sps;
	ssize_t size;
	unsigned int size_mb = SIZE_MACROBLOCK;
	/* Calculation of crop units in Rec. ITU-T H.264 (04/2017) p. 76 */
	unsigned int crop_unit_x = 2;
	unsigned int crop_unit_y = 2;

	sps = kzalloc(sizeof(*sps), GFP_KERNEL);
	if (!sps)
		return -ENOMEM;

	sps->profile_idc = nal_h264_profile_from_v4l2(channel->profile);
	sps->constraint_set0_flag = 0;
	sps->constraint_set1_flag = 1;
	sps->constraint_set2_flag = 0;
	sps->constraint_set3_flag = 0;
	sps->constraint_set4_flag = 0;
	sps->constraint_set5_flag = 0;
	sps->level_idc = nal_h264_level_from_v4l2(channel->level);
	sps->seq_parameter_set_id = 0;
	sps->log2_max_frame_num_minus4 = 0;
	sps->pic_order_cnt_type = 0;
	sps->log2_max_pic_order_cnt_lsb_minus4 = 6;
	sps->max_num_ref_frames = 3;
	sps->gaps_in_frame_num_value_allowed_flag = 0;
	sps->pic_width_in_mbs_minus1 =
		DIV_ROUND_UP(channel->width, size_mb) - 1;
	sps->pic_height_in_map_units_minus1 =
		DIV_ROUND_UP(channel->height, size_mb) - 1;
	sps->frame_mbs_only_flag = 1;
	sps->mb_adaptive_frame_field_flag = 0;
	sps->direct_8x8_inference_flag = 1;
	sps->frame_cropping_flag =
		(channel->width % size_mb) || (channel->height % size_mb);
	if (sps->frame_cropping_flag) {
		sps->crop_left = 0;
		sps->crop_right = (round_up(channel->width, size_mb) - channel->width) / crop_unit_x;
		sps->crop_top = 0;
		sps->crop_bottom = (round_up(channel->height, size_mb) - channel->height) / crop_unit_y;
	}
	sps->vui_parameters_present_flag = 1;
	sps->vui.aspect_ratio_info_present_flag = 0;
	sps->vui.overscan_info_present_flag = 0;
	sps->vui.video_signal_type_present_flag = 1;
	sps->vui.video_format = 1;
	sps->vui.video_full_range_flag = 0;
	sps->vui.colour_description_present_flag = 1;
	sps->vui.colour_primaries = 5;
	sps->vui.transfer_characteristics = 5;
	sps->vui.matrix_coefficients = 5;
	sps->vui.chroma_loc_info_present_flag = 1;
	sps->vui.chroma_sample_loc_type_top_field = 0;
	sps->vui.chroma_sample_loc_type_bottom_field = 0;

	sps->vui.timing_info_present_flag = 1;
	sps->vui.num_units_in_tick = channel->framerate.denominator;
	sps->vui.time_scale = 2 * channel->framerate.numerator;

	sps->vui.fixed_frame_rate_flag = 1;
	sps->vui.nal_hrd_parameters_present_flag = 0;
	sps->vui.vcl_hrd_parameters_present_flag = 1;
	sps->vui.vcl_hrd_parameters.cpb_cnt_minus1 = 0;
	sps->vui.vcl_hrd_parameters.bit_rate_scale = 0;
	sps->vui.vcl_hrd_parameters.cpb_size_scale = 1;
	/* See Rec. ITU-T H.264 (04/2017) p. 410 E-53 */
	sps->vui.vcl_hrd_parameters.bit_rate_value_minus1[0] =
		channel->bitrate_peak / (1 << (6 + sps->vui.vcl_hrd_parameters.bit_rate_scale)) - 1;
	/* See Rec. ITU-T H.264 (04/2017) p. 410 E-54 */
	sps->vui.vcl_hrd_parameters.cpb_size_value_minus1[0] =
		(channel->cpb_size * 1000) / (1 << (4 + sps->vui.vcl_hrd_parameters.cpb_size_scale)) - 1;
	sps->vui.vcl_hrd_parameters.cbr_flag[0] =
		!v4l2_ctrl_g_ctrl(channel->mpeg_video_frame_rc_enable);
	sps->vui.vcl_hrd_parameters.initial_cpb_removal_delay_length_minus1 = 31;
	sps->vui.vcl_hrd_parameters.cpb_removal_delay_length_minus1 = 31;
	sps->vui.vcl_hrd_parameters.dpb_output_delay_length_minus1 = 31;
	sps->vui.vcl_hrd_parameters.time_offset_length = 0;
	sps->vui.low_delay_hrd_flag = 0;
	sps->vui.pic_struct_present_flag = 1;
	sps->vui.bitstream_restriction_flag = 0;

	size = nal_h264_write_sps(&dev->plat_dev->dev, dest, n, sps);

	kfree(sps);

	return size;
}

static ssize_t allegro_h264_write_pps(struct allegro_channel *channel,
				      void *dest, size_t n)
{
	struct allegro_dev *dev = channel->dev;
	struct nal_h264_pps *pps;
	ssize_t size;

	pps = kzalloc(sizeof(*pps), GFP_KERNEL);
	if (!pps)
		return -ENOMEM;

	pps->pic_parameter_set_id = 0;
	pps->seq_parameter_set_id = 0;
	pps->entropy_coding_mode_flag = 0;
	pps->bottom_field_pic_order_in_frame_present_flag = 0;
	pps->num_slice_groups_minus1 = 0;
	pps->num_ref_idx_l0_default_active_minus1 = 2;
	pps->num_ref_idx_l1_default_active_minus1 = 2;
	pps->weighted_pred_flag = 0;
	pps->weighted_bipred_idc = 0;
	pps->pic_init_qp_minus26 = 0;
	pps->pic_init_qs_minus26 = 0;
	pps->chroma_qp_index_offset = 0;
	pps->deblocking_filter_control_present_flag = 1;
	pps->constrained_intra_pred_flag = 0;
	pps->redundant_pic_cnt_present_flag = 0;
	pps->transform_8x8_mode_flag = 0;
	pps->pic_scaling_matrix_present_flag = 0;
	pps->second_chroma_qp_index_offset = 0;

	size = nal_h264_write_pps(&dev->plat_dev->dev, dest, n, pps);

	kfree(pps);

	return size;
}

static bool allegro_channel_is_at_eos(struct allegro_channel *channel)
{
	bool is_at_eos = false;

	switch (allegro_get_state(channel)) {
	case ALLEGRO_STATE_STOPPED:
		is_at_eos = true;
		break;
	case ALLEGRO_STATE_DRAIN:
	case ALLEGRO_STATE_WAIT_FOR_BUFFER:
		mutex_lock(&channel->shadow_list_lock);
		if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) == 0 &&
		    list_empty(&channel->source_shadow_list))
			is_at_eos = true;
		mutex_unlock(&channel->shadow_list_lock);
		break;
	default:
		break;
	}

	return is_at_eos;
}

static void allegro_channel_buf_done(struct allegro_channel *channel,
				     struct vb2_v4l2_buffer *buf,
				     enum vb2_buffer_state state)
{
	const struct v4l2_event eos_event = {
		.type = V4L2_EVENT_EOS
	};

	if (allegro_channel_is_at_eos(channel)) {
		buf->flags |= V4L2_BUF_FLAG_LAST;
		v4l2_event_queue_fh(&channel->fh, &eos_event);

		allegro_set_state(channel, ALLEGRO_STATE_STOPPED);
	}

	v4l2_m2m_buf_done(buf, state);
}

static u64 allegro_put_buffer(struct allegro_channel *channel,
			      struct list_head *list,
			      struct vb2_v4l2_buffer *buffer)
{
	struct v4l2_m2m_buffer *b = container_of(buffer,
						 struct v4l2_m2m_buffer, vb);
	struct allegro_m2m_buffer *shadow = to_allegro_m2m_buffer(b);

	mutex_lock(&channel->shadow_list_lock);
	list_add_tail(&shadow->head, list);
	mutex_unlock(&channel->shadow_list_lock);

	return ptr_to_u64(buffer);
}

static struct vb2_v4l2_buffer *
allegro_get_buffer(struct allegro_channel *channel,
		   struct list_head *list, u64 handle)
{
	struct allegro_m2m_buffer *shadow, *tmp;
	struct vb2_v4l2_buffer *buffer = NULL;

	mutex_lock(&channel->shadow_list_lock);
	list_for_each_entry_safe(shadow, tmp, list, head) {
		if (handle == ptr_to_u64(&shadow->buf.vb)) {
			buffer = &shadow->buf.vb;
			list_del_init(&shadow->head);
			break;
		}
	}
	mutex_unlock(&channel->shadow_list_lock);

	return buffer;
}

static void allegro_channel_finish_frame(struct allegro_channel *channel,
		struct mcu_msg_encode_frame_response *msg)
{
	struct allegro_dev *dev = channel->dev;
	struct vb2_v4l2_buffer *src_buf;
	struct vb2_v4l2_buffer *dst_buf;
	struct {
		u32 offset;
		u32 size;
	} *partition;
	enum vb2_buffer_state state = VB2_BUF_STATE_ERROR;
	char *curr;
	ssize_t len;
	ssize_t free;

	src_buf = allegro_get_buffer(channel, &channel->source_shadow_list,
				     msg->src_handle);
	if (!src_buf)
		v4l2_warn(&dev->v4l2_dev,
			  "channel %d: invalid source buffer\n",
			  channel->mcu_channel_id);

	dst_buf = allegro_get_buffer(channel, &channel->stream_shadow_list,
				     msg->stream_id);
	if (!dst_buf)
		v4l2_warn(&dev->v4l2_dev,
			  "channel %d: invalid stream buffer\n",
			  channel->mcu_channel_id);

	if (!src_buf || !dst_buf)
		goto err;

	dst_buf->sequence = channel->csequence++;

	if (msg->error_code & AL_ERROR) {
		v4l2_err(&dev->v4l2_dev,
			 "channel %d: failed to encode frame: %s (%x)\n",
			 channel->mcu_channel_id,
			 allegro_err_to_string(msg->error_code),
			 msg->error_code);
		goto err;
	}

	if (msg->partition_table_size != 1) {
		v4l2_warn(&dev->v4l2_dev,
			  "channel %d: only handling first partition table entry (%d entries)\n",
			  channel->mcu_channel_id, msg->partition_table_size);
	}

	if (msg->partition_table_offset +
	    msg->partition_table_size * sizeof(*partition) >
	    vb2_plane_size(&dst_buf->vb2_buf, 0)) {
		v4l2_err(&dev->v4l2_dev,
			 "channel %d: partition table outside of dst_buf\n",
			 channel->mcu_channel_id);
		goto err;
	}

	partition =
	    vb2_plane_vaddr(&dst_buf->vb2_buf, 0) + msg->partition_table_offset;
	if (partition->offset + partition->size >
	    vb2_plane_size(&dst_buf->vb2_buf, 0)) {
		v4l2_err(&dev->v4l2_dev,
			 "channel %d: encoded frame is outside of dst_buf (offset 0x%x, size 0x%x)\n",
			 channel->mcu_channel_id, partition->offset,
			 partition->size);
		goto err;
	}

	v4l2_dbg(2, debug, &dev->v4l2_dev,
		 "channel %d: encoded frame of size %d is at offset 0x%x\n",
		 channel->mcu_channel_id, partition->size, partition->offset);

	/*
	 * The payload must include the data before the partition offset,
	 * because we will put the sps and pps data there.
	 */
	vb2_set_plane_payload(&dst_buf->vb2_buf, 0,
			      partition->offset + partition->size);

	curr = vb2_plane_vaddr(&dst_buf->vb2_buf, 0);
	free = partition->offset;
	if (msg->is_idr) {
		len = allegro_h264_write_sps(channel, curr, free);
		if (len < 0) {
			v4l2_err(&dev->v4l2_dev,
				 "not enough space for sequence parameter set: %zd left\n",
				 free);
			goto err;
		}
		curr += len;
		free -= len;
		v4l2_dbg(1, debug, &dev->v4l2_dev,
			 "channel %d: wrote %zd byte SPS nal unit\n",
			 channel->mcu_channel_id, len);
	}

	if (msg->slice_type == AL_ENC_SLICE_TYPE_I) {
		len = allegro_h264_write_pps(channel, curr, free);
		if (len < 0) {
			v4l2_err(&dev->v4l2_dev,
				 "not enough space for picture parameter set: %zd left\n",
				 free);
			goto err;
		}
		curr += len;
		free -= len;
		v4l2_dbg(1, debug, &dev->v4l2_dev,
			 "channel %d: wrote %zd byte PPS nal unit\n",
			 channel->mcu_channel_id, len);
	}

	if (msg->slice_type != AL_ENC_SLICE_TYPE_I && !msg->is_idr) {
		dst_buf->vb2_buf.planes[0].data_offset = free;
		free = 0;
	} else {
		len = nal_h264_write_filler(&dev->plat_dev->dev, curr, free);
		if (len < 0) {
			v4l2_err(&dev->v4l2_dev,
				 "failed to write %zd filler data\n", free);
			goto err;
		}
		curr += len;
		free -= len;
		v4l2_dbg(2, debug, &dev->v4l2_dev,
			 "channel %d: wrote %zd bytes filler nal unit\n",
			 channel->mcu_channel_id, len);
	}

	if (free != 0) {
		v4l2_err(&dev->v4l2_dev,
			 "non-VCL NAL units do not fill space until VCL NAL unit: %zd bytes left\n",
			 free);
		goto err;
	}

	state = VB2_BUF_STATE_DONE;

	v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false);
	if (msg->is_idr)
		dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME;
	else
		dst_buf->flags |= V4L2_BUF_FLAG_PFRAME;

	v4l2_dbg(1, debug, &dev->v4l2_dev,
		 "channel %d: encoded frame #%03d (%s%s, QP %d, %d bytes)\n",
		 channel->mcu_channel_id,
		 dst_buf->sequence,
		 msg->is_idr ? "IDR, " : "",
		 msg->slice_type == AL_ENC_SLICE_TYPE_I ? "I slice" :
		 msg->slice_type == AL_ENC_SLICE_TYPE_P ? "P slice" : "unknown",
		 msg->qp, partition->size);

err:
	if (src_buf)
		v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);

	if (dst_buf)
		allegro_channel_buf_done(channel, dst_buf, state);
}

static int allegro_handle_init(struct allegro_dev *dev,
			       struct mcu_msg_init_response *msg)
{
	complete(&dev->init_complete);

	return 0;
}

static int
allegro_handle_create_channel(struct allegro_dev *dev,
			      struct mcu_msg_create_channel_response *msg)
{
	struct allegro_channel *channel;
	int err = 0;

	if (msg->header.length != sizeof(*msg) - sizeof(msg->header))
		v4l2_warn(&dev->v4l2_dev,
			  "received message has %d bytes, but expected %zu\n",
			  msg->header.length,
			  sizeof(*msg) - sizeof(msg->header));

	channel = allegro_find_channel_by_user_id(dev, msg->user_id);
	if (IS_ERR(channel)) {
		v4l2_warn(&dev->v4l2_dev,
			  "received %s for unknown user %d\n",
			  msg_type_name(msg->header.type),
			  msg->user_id);
		return -EINVAL;
	}

	if (msg->error_code) {
		v4l2_err(&dev->v4l2_dev,
			 "user %d: mcu failed to create channel: %s (%x)\n",
			 channel->user_id,
			 allegro_err_to_string(msg->error_code),
			 msg->error_code);
		err = -EIO;
		goto out;
	}

	channel->mcu_channel_id = msg->channel_id;
	v4l2_dbg(1, debug, &dev->v4l2_dev,
		 "user %d: channel has channel id %d\n",
		 channel->user_id, channel->mcu_channel_id);

	v4l2_dbg(1, debug, &dev->v4l2_dev,
		 "channel %d: intermediate buffers: %d x %d bytes\n",
		 channel->mcu_channel_id,
		 msg->int_buffers_count, msg->int_buffers_size);
	err = allocate_intermediate_buffers(channel, msg->int_buffers_count,
					    msg->int_buffers_size);
	if (err) {
		v4l2_err(&dev->v4l2_dev,
			 "channel %d: failed to allocate intermediate buffers\n",
			 channel->mcu_channel_id);
		goto out;
	}
	err = allegro_mcu_push_buffer_intermediate(channel);
	if (err)
		goto out;

	v4l2_dbg(1, debug, &dev->v4l2_dev,
		 "channel %d: reference buffers: %d x %d bytes\n",
		 channel->mcu_channel_id,
		 msg->rec_buffers_count, msg->rec_buffers_size);
	err = allocate_reference_buffers(channel, msg->rec_buffers_count,
					 msg->rec_buffers_size);
	if (err) {
		v4l2_err(&dev->v4l2_dev,
			 "channel %d: failed to allocate reference buffers\n",
			 channel->mcu_channel_id);
		goto out;
	}
	err = allegro_mcu_push_buffer_reference(channel);
	if (err)
		goto out;

out:
	channel->error = err;
	complete(&channel->completion);

	/* Handled successfully, error is passed via channel->error */
	return 0;
}

static int
allegro_handle_destroy_channel(struct allegro_dev *dev,
			       struct mcu_msg_destroy_channel_response *msg)
{
	struct allegro_channel *channel;

	channel = allegro_find_channel_by_channel_id(dev, msg->channel_id);
	if (IS_ERR(channel)) {
		v4l2_err(&dev->v4l2_dev,
			 "received %s for unknown channel %d\n",
			 msg_type_name(msg->header.type),
			 msg->channel_id);
		return -EINVAL;
	}

	v4l2_dbg(2, debug, &dev->v4l2_dev,
		 "user %d: vcu destroyed channel %d\n",
		 channel->user_id, channel->mcu_channel_id);
	complete(&channel->completion);

	return 0;
}

static int
allegro_handle_encode_frame(struct allegro_dev *dev,
			    struct mcu_msg_encode_frame_response *msg)
{
	struct allegro_channel *channel;

	if (msg->header.length != sizeof(*msg) - sizeof(msg->header))
		v4l2_warn(&dev->v4l2_dev,
			  "received message has %d bytes, but expected %zu\n",
			  msg->header.length,
			  sizeof(*msg) - sizeof(msg->header));

	channel = allegro_find_channel_by_channel_id(dev, msg->channel_id);
	if (IS_ERR(channel)) {
		v4l2_err(&dev->v4l2_dev,
			 "received %s for unknown channel %d\n",
			 msg_type_name(msg->header.type),
			 msg->channel_id);
		return -EINVAL;
	}

	allegro_channel_finish_frame(channel, msg);

	return 0;
}

static int allegro_receive_message(struct allegro_dev *dev)
{
	union mcu_msg_response *msg;
	ssize_t size;
	int err = 0;

	msg = kmalloc(sizeof(*msg), GFP_KERNEL);
	if (!msg)
		return -ENOMEM;

	size = allegro_mbox_read(dev, &dev->mbox_status, msg, sizeof(*msg));
	if (size < sizeof(msg->header)) {
		v4l2_err(&dev->v4l2_dev,
			 "invalid mbox message (%zd): must be at least %zu\n",
			 size, sizeof(msg->header));
		err = -EINVAL;
		goto out;
	}

	switch (msg->header.type) {
	case MCU_MSG_TYPE_INIT:
		err = allegro_handle_init(dev, &msg->init);
		break;
	case MCU_MSG_TYPE_CREATE_CHANNEL:
		err = allegro_handle_create_channel(dev, &msg->create_channel);
		break;
	case MCU_MSG_TYPE_DESTROY_CHANNEL:
		err = allegro_handle_destroy_channel(dev,
						     &msg->destroy_channel);
		break;
	case MCU_MSG_TYPE_ENCODE_FRAME:
		err = allegro_handle_encode_frame(dev, &msg->encode_frame);
		break;
	default:
		v4l2_warn(&dev->v4l2_dev,
			  "%s: unknown message %s\n",
			  __func__, msg_type_name(msg->header.type));
		err = -EINVAL;
		break;
	}

out:
	kfree(msg);

	return err;
}

static irqreturn_t allegro_hardirq(int irq, void *data)
{
	struct allegro_dev *dev = data;
	unsigned int status;

	regmap_read(dev->regmap, AL5_ITC_CPU_IRQ_STA, &status);
	if (!(status & AL5_ITC_CPU_IRQ_STA_TRIGGERED))
		return IRQ_NONE;

	regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_CLR, status);

	return IRQ_WAKE_THREAD;
}

static irqreturn_t allegro_irq_thread(int irq, void *data)
{
	struct allegro_dev *dev = data;

	allegro_receive_message(dev);

	return IRQ_HANDLED;
}

static void allegro_copy_firmware(struct allegro_dev *dev,
				  const u8 * const buf, size_t size)
{
	int err = 0;

	v4l2_dbg(1, debug, &dev->v4l2_dev,
		 "copy mcu firmware (%zu B) to SRAM\n", size);
	err = regmap_bulk_write(dev->sram, 0x0, buf, size / 4);
	if (err)
		v4l2_err(&dev->v4l2_dev,
			 "failed to copy firmware: %d\n", err);
}

static void allegro_copy_fw_codec(struct allegro_dev *dev,
				  const u8 * const buf, size_t size)
{
	int err;
	dma_addr_t icache_offset, dcache_offset;

	/*
	 * The downstream allocates 600 KB for the codec firmware to have some
	 * extra space for "possible extensions." My tests were fine with
	 * allocating just enough memory for the actual firmware, but I am not
	 * sure that the firmware really does not use the remaining space.
	 */
	err = allegro_alloc_buffer(dev, &dev->firmware, size);
	if (err) {
		v4l2_err(&dev->v4l2_dev,
			 "failed to allocate %zu bytes for firmware\n", size);
		return;
	}

	v4l2_dbg(1, debug, &dev->v4l2_dev,
		 "copy codec firmware (%zd B) to phys %pad\n",
		 size, &dev->firmware.paddr);
	memcpy(dev->firmware.vaddr, buf, size);

	regmap_write(dev->regmap, AXI_ADDR_OFFSET_IP,
		     upper_32_bits(dev->firmware.paddr));

	icache_offset = dev->firmware.paddr - MCU_CACHE_OFFSET;
	v4l2_dbg(2, debug, &dev->v4l2_dev,
		 "icache_offset: msb = 0x%x, lsb = 0x%x\n",
		 upper_32_bits(icache_offset), lower_32_bits(icache_offset));
	regmap_write(dev->regmap, AL5_ICACHE_ADDR_OFFSET_MSB,
		     upper_32_bits(icache_offset));
	regmap_write(dev->regmap, AL5_ICACHE_ADDR_OFFSET_LSB,
		     lower_32_bits(icache_offset));

	dcache_offset =
	    (dev->firmware.paddr & 0xffffffff00000000ULL) - MCU_CACHE_OFFSET;
	v4l2_dbg(2, debug, &dev->v4l2_dev,
		 "dcache_offset: msb = 0x%x, lsb = 0x%x\n",
		 upper_32_bits(dcache_offset), lower_32_bits(dcache_offset));
	regmap_write(dev->regmap, AL5_DCACHE_ADDR_OFFSET_MSB,
		     upper_32_bits(dcache_offset));
	regmap_write(dev->regmap, AL5_DCACHE_ADDR_OFFSET_LSB,
		     lower_32_bits(dcache_offset));
}

static void allegro_free_fw_codec(struct allegro_dev *dev)
{
	allegro_free_buffer(dev, &dev->firmware);
}

/*
 * Control functions for the MCU
 */

static int allegro_mcu_enable_interrupts(struct allegro_dev *dev)
{
	return regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_MSK, BIT(0));
}

static int allegro_mcu_disable_interrupts(struct allegro_dev *dev)
{
	return regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_MSK, 0);
}

static int allegro_mcu_wait_for_sleep(struct allegro_dev *dev)
{
	unsigned long timeout;
	unsigned int status;

	timeout = jiffies + msecs_to_jiffies(100);
	while (regmap_read(dev->regmap, AL5_MCU_STA, &status) == 0 &&
	       status != AL5_MCU_STA_SLEEP) {
		if (time_after(jiffies, timeout))
			return -ETIMEDOUT;
		cpu_relax();
	}

	return 0;
}

static int allegro_mcu_start(struct allegro_dev *dev)
{
	unsigned long timeout;
	unsigned int status;
	int err;

	err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, BIT(0));
	if (err)
		return err;

	timeout = jiffies + msecs_to_jiffies(100);
	while (regmap_read(dev->regmap, AL5_MCU_STA, &status) == 0 &&
	       status == AL5_MCU_STA_SLEEP) {
		if (time_after(jiffies, timeout))
			return -ETIMEDOUT;
		cpu_relax();
	}

	err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, 0);
	if (err)
		return err;

	return 0;
}

static int allegro_mcu_reset(struct allegro_dev *dev)
{
	int err;

	/*
	 * Ensure that the AL5_MCU_WAKEUP bit is set to 0 otherwise the mcu
	 * does not go to sleep after the reset.
	 */
	err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, 0);
	if (err)
		return err;

	err = regmap_write(dev->regmap,
			   AL5_MCU_RESET_MODE, AL5_MCU_RESET_MODE_SLEEP);
	if (err < 0)
		return err;

	err = regmap_write(dev->regmap, AL5_MCU_RESET, AL5_MCU_RESET_SOFT);
	if (err < 0)
		return err;

	return allegro_mcu_wait_for_sleep(dev);
}

static void allegro_destroy_channel(struct allegro_channel *channel)
{
	struct allegro_dev *dev = channel->dev;
	unsigned long timeout;

	if (channel_exists(channel)) {
		reinit_completion(&channel->completion);
		allegro_mcu_send_destroy_channel(dev, channel);
		timeout = wait_for_completion_timeout(&channel->completion,
						      msecs_to_jiffies(5000));
		if (timeout == 0)
			v4l2_warn(&dev->v4l2_dev,
				  "channel %d: timeout while destroying\n",
				  channel->mcu_channel_id);

		channel->mcu_channel_id = -1;
	}

	destroy_intermediate_buffers(channel);
	destroy_reference_buffers(channel);

	v4l2_ctrl_grab(channel->mpeg_video_h264_profile, false);
	v4l2_ctrl_grab(channel->mpeg_video_h264_level, false);
	v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, false);
	v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, false);
	v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, false);
	v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, false);
	v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, false);
	v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, false);
	v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, false);
	v4l2_ctrl_grab(channel->mpeg_video_bitrate, false);
	v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, false);
	v4l2_ctrl_grab(channel->mpeg_video_cpb_size, false);
	v4l2_ctrl_grab(channel->mpeg_video_gop_size, false);

	if (channel->user_id != -1) {
		clear_bit(channel->user_id, &dev->channel_user_ids);
		channel->user_id = -1;
	}
}

/*
 * Create the MCU channel
 *
 * After the channel has been created, the picture size, format, colorspace
 * and framerate are fixed. Also the codec, profile, bitrate, etc. cannot be
 * changed anymore.
 *
 * The channel can be created only once. The MCU will accept source buffers
 * and stream buffers only after a channel has been created.
 */
static int allegro_create_channel(struct allegro_channel *channel)
{
	struct allegro_dev *dev = channel->dev;
	unsigned long timeout;
	enum v4l2_mpeg_video_h264_level min_level;

	if (channel_exists(channel)) {
		v4l2_warn(&dev->v4l2_dev,
			  "channel already exists\n");
		return 0;
	}

	channel->user_id = allegro_next_user_id(dev);
	if (channel->user_id < 0) {
		v4l2_err(&dev->v4l2_dev,
			 "no free channels available\n");
		return -EBUSY;
	}
	set_bit(channel->user_id, &dev->channel_user_ids);

	v4l2_dbg(1, debug, &dev->v4l2_dev,
		 "user %d: creating channel (%4.4s, %dx%d@%d)\n",
		 channel->user_id,
		 (char *)&channel->codec, channel->width, channel->height,
		 DIV_ROUND_UP(channel->framerate.numerator,
			      channel->framerate.denominator));

	min_level = select_minimum_h264_level(channel->width, channel->height);
	if (channel->level < min_level) {
		v4l2_warn(&dev->v4l2_dev,
			  "user %d: selected Level %s too low: increasing to Level %s\n",
			  channel->user_id,
			  v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL)[channel->level],
			  v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL)[min_level]);
		channel->level = min_level;
	}

	v4l2_ctrl_grab(channel->mpeg_video_h264_profile, true);
	v4l2_ctrl_grab(channel->mpeg_video_h264_level, true);
	v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, true);
	v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, true);
	v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, true);
	v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, true);
	v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, true);
	v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, true);
	v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, true);
	v4l2_ctrl_grab(channel->mpeg_video_bitrate, true);
	v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, true);
	v4l2_ctrl_grab(channel->mpeg_video_cpb_size, true);
	v4l2_ctrl_grab(channel->mpeg_video_gop_size, true);

	reinit_completion(&channel->completion);
	allegro_mcu_send_create_channel(dev, channel);
	timeout = wait_for_completion_timeout(&channel->completion,
					      msecs_to_jiffies(5000));
	if (timeout == 0)
		channel->error = -ETIMEDOUT;
	if (channel->error)
		goto err;

	v4l2_dbg(1, debug, &dev->v4l2_dev,
		 "channel %d: accepting buffers\n",
		 channel->mcu_channel_id);

	return 0;

err:
	allegro_destroy_channel(channel);

	return channel->error;
}

static void allegro_set_default_params(struct allegro_channel *channel)
{
	channel->width = ALLEGRO_WIDTH_DEFAULT;
	channel->height = ALLEGRO_HEIGHT_DEFAULT;
	channel->stride = round_up(channel->width, 32);
	channel->framerate = ALLEGRO_FRAMERATE_DEFAULT;

	channel->colorspace = V4L2_COLORSPACE_REC709;
	channel->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
	channel->quantization = V4L2_QUANTIZATION_DEFAULT;
	channel->xfer_func = V4L2_XFER_FUNC_DEFAULT;

	channel->pixelformat = V4L2_PIX_FMT_NV12;
	channel->sizeimage_raw = channel->stride * channel->height * 3 / 2;

	channel->codec = V4L2_PIX_FMT_H264;
	channel->profile = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE;
	channel->level =
		select_minimum_h264_level(channel->width, channel->height);
	channel->sizeimage_encoded =
		estimate_stream_size(channel->width, channel->height);

	channel->bitrate = maximum_bitrate(channel->level);
	channel->bitrate_peak = maximum_bitrate(channel->level);
	channel->cpb_size = maximum_cpb_size(channel->level);
	channel->gop_size = ALLEGRO_GOP_SIZE_DEFAULT;
}

static int allegro_queue_setup(struct vb2_queue *vq,
			       unsigned int *nbuffers, unsigned int *nplanes,
			       unsigned int sizes[],
			       struct device *alloc_devs[])
{
	struct allegro_channel *channel = vb2_get_drv_priv(vq);
	struct allegro_dev *dev = channel->dev;

	v4l2_dbg(2, debug, &dev->v4l2_dev,
		 "%s: queue setup[%s]: nplanes = %d\n",
		 V4L2_TYPE_IS_OUTPUT(vq->type) ? "output" : "capture",
		 *nplanes == 0 ? "REQBUFS" : "CREATE_BUFS", *nplanes);

	if (*nplanes != 0) {
		if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
			if (sizes[0] < channel->sizeimage_raw)
				return -EINVAL;
		} else {
			if (sizes[0] < channel->sizeimage_encoded)
				return -EINVAL;
		}
	} else {
		*nplanes = 1;
		if (V4L2_TYPE_IS_OUTPUT(vq->type))
			sizes[0] = channel->sizeimage_raw;
		else
			sizes[0] = channel->sizeimage_encoded;
	}

	return 0;
}

static int allegro_buf_prepare(struct vb2_buffer *vb)
{
	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
	struct allegro_channel *channel = vb2_get_drv_priv(vb->vb2_queue);
	struct allegro_dev *dev = channel->dev;

	if (allegro_get_state(channel) == ALLEGRO_STATE_DRAIN &&
	    V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type))
		return -EBUSY;

	if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
		if (vbuf->field == V4L2_FIELD_ANY)
			vbuf->field = V4L2_FIELD_NONE;
		if (vbuf->field != V4L2_FIELD_NONE) {
			v4l2_err(&dev->v4l2_dev,
				 "channel %d: unsupported field\n",
				 channel->mcu_channel_id);
			return -EINVAL;
		}
	}

	return 0;
}

static void allegro_buf_queue(struct vb2_buffer *vb)
{
	struct allegro_channel *channel = vb2_get_drv_priv(vb->vb2_queue);
	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);

	if (allegro_get_state(channel) == ALLEGRO_STATE_WAIT_FOR_BUFFER &&
	    vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
		allegro_channel_buf_done(channel, vbuf, VB2_BUF_STATE_DONE);
		return;
	}

	v4l2_m2m_buf_queue(channel->fh.m2m_ctx, vbuf);
}

static int allegro_start_streaming(struct vb2_queue *q, unsigned int count)
{
	struct allegro_channel *channel = vb2_get_drv_priv(q);
	struct allegro_dev *dev = channel->dev;

	v4l2_dbg(2, debug, &dev->v4l2_dev,
		 "%s: start streaming\n",
		 V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture");

	if (V4L2_TYPE_IS_OUTPUT(q->type)) {
		channel->osequence = 0;
		allegro_set_state(channel, ALLEGRO_STATE_ENCODING);
	} else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
		channel->csequence = 0;
	}

	return 0;
}

static void allegro_stop_streaming(struct vb2_queue *q)
{
	struct allegro_channel *channel = vb2_get_drv_priv(q);
	struct allegro_dev *dev = channel->dev;
	struct vb2_v4l2_buffer *buffer;
	struct allegro_m2m_buffer *shadow, *tmp;

	v4l2_dbg(2, debug, &dev->v4l2_dev,
		 "%s: stop streaming\n",
		 V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture");

	if (V4L2_TYPE_IS_OUTPUT(q->type)) {
		mutex_lock(&channel->shadow_list_lock);
		list_for_each_entry_safe(shadow, tmp,
					 &channel->source_shadow_list, head) {
			list_del(&shadow->head);
			v4l2_m2m_buf_done(&shadow->buf.vb, VB2_BUF_STATE_ERROR);
		}
		mutex_unlock(&channel->shadow_list_lock);

		allegro_set_state(channel, ALLEGRO_STATE_STOPPED);
		while ((buffer = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx)))
			v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR);
	} else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
		mutex_lock(&channel->shadow_list_lock);
		list_for_each_entry_safe(shadow, tmp,
					 &channel->stream_shadow_list, head) {
			list_del(&shadow->head);
			v4l2_m2m_buf_done(&shadow->buf.vb, VB2_BUF_STATE_ERROR);
		}
		mutex_unlock(&channel->shadow_list_lock);

		allegro_destroy_channel(channel);
		while ((buffer = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx)))
			v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR);
	}
}

static const struct vb2_ops allegro_queue_ops = {
	.queue_setup = allegro_queue_setup,
	.buf_prepare = allegro_buf_prepare,
	.buf_queue = allegro_buf_queue,
	.start_streaming = allegro_start_streaming,
	.stop_streaming = allegro_stop_streaming,
	.wait_prepare = vb2_ops_wait_prepare,
	.wait_finish = vb2_ops_wait_finish,
};

static int allegro_queue_init(void *priv,
			      struct vb2_queue *src_vq,
			      struct vb2_queue *dst_vq)
{
	int err;
	struct allegro_channel *channel = priv;

	src_vq->dev = &channel->dev->plat_dev->dev;
	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
	src_vq->io_modes = VB2_DMABUF | VB2_MMAP;
	src_vq->mem_ops = &vb2_dma_contig_memops;
	src_vq->drv_priv = channel;
	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
	src_vq->ops = &allegro_queue_ops;
	src_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer);
	src_vq->lock = &channel->dev->lock;
	err = vb2_queue_init(src_vq);
	if (err)
		return err;

	dst_vq->dev = &channel->dev->plat_dev->dev;
	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	dst_vq->io_modes = VB2_DMABUF | VB2_MMAP;
	dst_vq->mem_ops = &vb2_dma_contig_memops;
	dst_vq->drv_priv = channel;
	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
	dst_vq->ops = &allegro_queue_ops;
	dst_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer);
	dst_vq->lock = &channel->dev->lock;
	err = vb2_queue_init(dst_vq);
	if (err)
		return err;

	return 0;
}

static int allegro_clamp_qp(struct allegro_channel *channel,
			    struct v4l2_ctrl *ctrl)
{
	struct v4l2_ctrl *next_ctrl;

	if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP)
		next_ctrl = channel->mpeg_video_h264_p_frame_qp;
	else if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP)
		next_ctrl = channel->mpeg_video_h264_b_frame_qp;
	else
		return 0;

	/* Modify range automatically updates the value */
	__v4l2_ctrl_modify_range(next_ctrl, ctrl->val, 51, 1, ctrl->val);

	return allegro_clamp_qp(channel, next_ctrl);
}

static int allegro_clamp_bitrate(struct allegro_channel *channel,
				 struct v4l2_ctrl *ctrl)
{
	struct v4l2_ctrl *ctrl_bitrate = channel->mpeg_video_bitrate;
	struct v4l2_ctrl *ctrl_bitrate_peak = channel->mpeg_video_bitrate_peak;

	if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
	    ctrl_bitrate_peak->val < ctrl_bitrate->val)
		ctrl_bitrate_peak->val = ctrl_bitrate->val;

	return 0;
}

static int allegro_try_ctrl(struct v4l2_ctrl *ctrl)
{
	struct allegro_channel *channel = container_of(ctrl->handler,
						       struct allegro_channel,
						       ctrl_handler);

	switch (ctrl->id) {
	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
		allegro_clamp_bitrate(channel, ctrl);
		break;
	}

	return 0;
}

static int allegro_s_ctrl(struct v4l2_ctrl *ctrl)
{
	struct allegro_channel *channel = container_of(ctrl->handler,
						       struct allegro_channel,
						       ctrl_handler);
	struct allegro_dev *dev = channel->dev;

	v4l2_dbg(1, debug, &dev->v4l2_dev,
		 "s_ctrl: %s = %d\n", v4l2_ctrl_get_name(ctrl->id), ctrl->val);

	switch (ctrl->id) {
	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
		channel->level = ctrl->val;
		break;
	case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
		channel->frame_rc_enable = ctrl->val;
		break;
	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
		channel->bitrate = channel->mpeg_video_bitrate->val;
		channel->bitrate_peak = channel->mpeg_video_bitrate_peak->val;
		v4l2_ctrl_activate(channel->mpeg_video_bitrate_peak,
				   ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
		break;
	case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE:
		channel->cpb_size = ctrl->val;
		break;
	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
		channel->gop_size = ctrl->val;
		break;
	case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
	case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
	case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
		allegro_clamp_qp(channel, ctrl);
		break;
	}

	return 0;
}

static const struct v4l2_ctrl_ops allegro_ctrl_ops = {
	.try_ctrl = allegro_try_ctrl,
	.s_ctrl = allegro_s_ctrl,
};

static int allegro_open(struct file *file)
{
	struct video_device *vdev = video_devdata(file);
	struct allegro_dev *dev = video_get_drvdata(vdev);
	struct allegro_channel *channel = NULL;
	struct v4l2_ctrl_handler *handler;
	u64 mask;
	int ret;

	channel = kzalloc(sizeof(*channel), GFP_KERNEL);
	if (!channel)
		return -ENOMEM;

	v4l2_fh_init(&channel->fh, vdev);

	init_completion(&channel->completion);
	INIT_LIST_HEAD(&channel->source_shadow_list);
	INIT_LIST_HEAD(&channel->stream_shadow_list);
	mutex_init(&channel->shadow_list_lock);

	channel->dev = dev;

	allegro_set_default_params(channel);

	handler = &channel->ctrl_handler;
	v4l2_ctrl_handler_init(handler, 0);
	channel->mpeg_video_h264_profile = v4l2_ctrl_new_std_menu(handler,
			&allegro_ctrl_ops,
			V4L2_CID_MPEG_VIDEO_H264_PROFILE,
			V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, 0x0,
			V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE);
	mask = 1 << V4L2_MPEG_VIDEO_H264_LEVEL_1B;
	channel->mpeg_video_h264_level = v4l2_ctrl_new_std_menu(handler,
			&allegro_ctrl_ops,
			V4L2_CID_MPEG_VIDEO_H264_LEVEL,
			V4L2_MPEG_VIDEO_H264_LEVEL_5_1, mask,
			V4L2_MPEG_VIDEO_H264_LEVEL_5_1);
	channel->mpeg_video_h264_i_frame_qp =
		v4l2_ctrl_new_std(handler,
				  &allegro_ctrl_ops,
				  V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP,
				  0, 51, 1, 30);
	channel->mpeg_video_h264_max_qp =
		v4l2_ctrl_new_std(handler,
				  &allegro_ctrl_ops,
				  V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
				  0, 51, 1, 51);
	channel->mpeg_video_h264_min_qp =
		v4l2_ctrl_new_std(handler,
				  &allegro_ctrl_ops,
				  V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
				  0, 51, 1, 0);
	channel->mpeg_video_h264_p_frame_qp =
		v4l2_ctrl_new_std(handler,
				  &allegro_ctrl_ops,
				  V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP,
				  0, 51, 1, 30);
	channel->mpeg_video_h264_b_frame_qp =
		v4l2_ctrl_new_std(handler,
				  &allegro_ctrl_ops,
				  V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP,
				  0, 51, 1, 30);
	channel->mpeg_video_frame_rc_enable =
		v4l2_ctrl_new_std(handler,
				  &allegro_ctrl_ops,
				  V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE,
				  false, 0x1,
				  true, false);
	channel->mpeg_video_bitrate_mode = v4l2_ctrl_new_std_menu(handler,
			&allegro_ctrl_ops,
			V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
			V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
			V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
	channel->mpeg_video_bitrate = v4l2_ctrl_new_std(handler,
			&allegro_ctrl_ops,
			V4L2_CID_MPEG_VIDEO_BITRATE,
			0, maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1),
			1, channel->bitrate);
	channel->mpeg_video_bitrate_peak = v4l2_ctrl_new_std(handler,
			&allegro_ctrl_ops,
			V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
			0, maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1),
			1, channel->bitrate_peak);
	channel->mpeg_video_cpb_size = v4l2_ctrl_new_std(handler,
			&allegro_ctrl_ops,
			V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE,
			0, maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1),
			1, channel->cpb_size);
	channel->mpeg_video_gop_size = v4l2_ctrl_new_std(handler,
			&allegro_ctrl_ops,
			V4L2_CID_MPEG_VIDEO_GOP_SIZE,
			0, ALLEGRO_GOP_SIZE_MAX,
			1, channel->gop_size);
	v4l2_ctrl_new_std(handler,
			  &allegro_ctrl_ops,
			  V4L2_CID_MIN_BUFFERS_FOR_OUTPUT,
			  1, 32,
			  1, 1);
	if (handler->error != 0) {
		ret = handler->error;
		goto error;
	}

	channel->fh.ctrl_handler = handler;

	v4l2_ctrl_cluster(3, &channel->mpeg_video_bitrate_mode);

	channel->mcu_channel_id = -1;
	channel->user_id = -1;

	INIT_LIST_HEAD(&channel->buffers_reference);
	INIT_LIST_HEAD(&channel->buffers_intermediate);

	list_add(&channel->list, &dev->channels);

	channel->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, channel,
						allegro_queue_init);

	if (IS_ERR(channel->fh.m2m_ctx)) {
		ret = PTR_ERR(channel->fh.m2m_ctx);
		goto error;
	}

	file->private_data = &channel->fh;
	v4l2_fh_add(&channel->fh);

	return 0;

error:
	v4l2_ctrl_handler_free(handler);
	kfree(channel);
	return ret;
}

static int allegro_release(struct file *file)
{
	struct allegro_channel *channel = fh_to_channel(file->private_data);

	v4l2_m2m_ctx_release(channel->fh.m2m_ctx);

	list_del(&channel->list);

	v4l2_ctrl_handler_free(&channel->ctrl_handler);

	v4l2_fh_del(&channel->fh);
	v4l2_fh_exit(&channel->fh);

	kfree(channel);

	return 0;
}

static int allegro_querycap(struct file *file, void *fh,
			    struct v4l2_capability *cap)
{
	struct video_device *vdev = video_devdata(file);
	struct allegro_dev *dev = video_get_drvdata(vdev);

	strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
	strscpy(cap->card, "Allegro DVT Video Encoder", sizeof(cap->card));
	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
		 dev_name(&dev->plat_dev->dev));

	return 0;
}

static int allegro_enum_fmt_vid(struct file *file, void *fh,
				struct v4l2_fmtdesc *f)
{
	if (f->index)
		return -EINVAL;
	switch (f->type) {
	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
		f->pixelformat = V4L2_PIX_FMT_NV12;
		break;
	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
		f->pixelformat = V4L2_PIX_FMT_H264;
		break;
	default:
		return -EINVAL;
	}
	return 0;
}

static int allegro_g_fmt_vid_cap(struct file *file, void *fh,
				 struct v4l2_format *f)
{
	struct allegro_channel *channel = fh_to_channel(fh);

	f->fmt.pix.field = V4L2_FIELD_NONE;
	f->fmt.pix.width = channel->width;
	f->fmt.pix.height = channel->height;

	f->fmt.pix.colorspace = channel->colorspace;
	f->fmt.pix.ycbcr_enc = channel->ycbcr_enc;
	f->fmt.pix.quantization = channel->quantization;
	f->fmt.pix.xfer_func = channel->xfer_func;

	f->fmt.pix.pixelformat = channel->codec;
	f->fmt.pix.bytesperline = 0;
	f->fmt.pix.sizeimage = channel->sizeimage_encoded;

	return 0;
}

static int allegro_try_fmt_vid_cap(struct file *file, void *fh,
				   struct v4l2_format *f)
{
	f->fmt.pix.field = V4L2_FIELD_NONE;

	f->fmt.pix.width = clamp_t(__u32, f->fmt.pix.width,
				   ALLEGRO_WIDTH_MIN, ALLEGRO_WIDTH_MAX);
	f->fmt.pix.height = clamp_t(__u32, f->fmt.pix.height,
				    ALLEGRO_HEIGHT_MIN, ALLEGRO_HEIGHT_MAX);

	f->fmt.pix.pixelformat = V4L2_PIX_FMT_H264;
	f->fmt.pix.bytesperline = 0;
	f->fmt.pix.sizeimage =
		estimate_stream_size(f->fmt.pix.width, f->fmt.pix.height);

	return 0;
}

static int allegro_g_fmt_vid_out(struct file *file, void *fh,
				 struct v4l2_format *f)
{
	struct allegro_channel *channel = fh_to_channel(fh);

	f->fmt.pix.field = V4L2_FIELD_NONE;

	f->fmt.pix.width = channel->width;
	f->fmt.pix.height = channel->height;

	f->fmt.pix.colorspace = channel->colorspace;
	f->fmt.pix.ycbcr_enc = channel->ycbcr_enc;
	f->fmt.pix.quantization = channel->quantization;
	f->fmt.pix.xfer_func = channel->xfer_func;

	f->fmt.pix.pixelformat = channel->pixelformat;
	f->fmt.pix.bytesperline = channel->stride;
	f->fmt.pix.sizeimage = channel->sizeimage_raw;

	return 0;
}

static int allegro_try_fmt_vid_out(struct file *file, void *fh,
				   struct v4l2_format *f)
{
	f->fmt.pix.field = V4L2_FIELD_NONE;

	/*
	 * The firmware of the Allegro codec handles the padding internally
	 * and expects the visual frame size when configuring a channel.
	 * Therefore, unlike other encoder drivers, this driver does not round
	 * up the width and height to macroblock alignment and does not
	 * implement the selection api.
	 */
	f->fmt.pix.width = clamp_t(__u32, f->fmt.pix.width,
				   ALLEGRO_WIDTH_MIN, ALLEGRO_WIDTH_MAX);
	f->fmt.pix.height = clamp_t(__u32, f->fmt.pix.height,
				    ALLEGRO_HEIGHT_MIN, ALLEGRO_HEIGHT_MAX);

	f->fmt.pix.pixelformat = V4L2_PIX_FMT_NV12;
	f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 32);
	f->fmt.pix.sizeimage =
		f->fmt.pix.bytesperline * f->fmt.pix.height * 3 / 2;

	return 0;
}

static int allegro_s_fmt_vid_out(struct file *file, void *fh,
				 struct v4l2_format *f)
{
	struct allegro_channel *channel = fh_to_channel(fh);
	int err;

	err = allegro_try_fmt_vid_out(file, fh, f);
	if (err)
		return err;

	channel->width = f->fmt.pix.width;
	channel->height = f->fmt.pix.height;
	channel->stride = f->fmt.pix.bytesperline;
	channel->sizeimage_raw = f->fmt.pix.sizeimage;

	channel->colorspace = f->fmt.pix.colorspace;
	channel->ycbcr_enc = f->fmt.pix.ycbcr_enc;
	channel->quantization = f->fmt.pix.quantization;
	channel->xfer_func = f->fmt.pix.xfer_func;

	channel->level =
		select_minimum_h264_level(channel->width, channel->height);
	channel->sizeimage_encoded =
		estimate_stream_size(channel->width, channel->height);

	return 0;
}

static int allegro_channel_cmd_stop(struct allegro_channel *channel)
{
	struct allegro_dev *dev = channel->dev;
	struct vb2_v4l2_buffer *dst_buf;

	switch (allegro_get_state(channel)) {
	case ALLEGRO_STATE_DRAIN:
	case ALLEGRO_STATE_WAIT_FOR_BUFFER:
		return -EBUSY;
	case ALLEGRO_STATE_ENCODING:
		allegro_set_state(channel, ALLEGRO_STATE_DRAIN);
		break;
	default:
		return 0;
	}

	/* If there are output buffers, they must be encoded */
	if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) != 0) {
		v4l2_dbg(1, debug,  &dev->v4l2_dev,
			 "channel %d: CMD_STOP: continue encoding src buffers\n",
			 channel->mcu_channel_id);
		return 0;
	}

	/* If there are capture buffers, use it to signal EOS */
	dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx);
	if (dst_buf) {
		v4l2_dbg(1, debug,  &dev->v4l2_dev,
			 "channel %d: CMD_STOP: signaling EOS\n",
			 channel->mcu_channel_id);
		allegro_channel_buf_done(channel, dst_buf, VB2_BUF_STATE_DONE);
		return 0;
	}

	/*
	 * If there are no capture buffers, we need to wait for the next
	 * buffer to signal EOS.
	 */
	v4l2_dbg(1, debug,  &dev->v4l2_dev,
		 "channel %d: CMD_STOP: wait for CAPTURE buffer to signal EOS\n",
		 channel->mcu_channel_id);
	allegro_set_state(channel, ALLEGRO_STATE_WAIT_FOR_BUFFER);

	return 0;
}

static int allegro_channel_cmd_start(struct allegro_channel *channel)
{
	switch (allegro_get_state(channel)) {
	case ALLEGRO_STATE_DRAIN:
	case ALLEGRO_STATE_WAIT_FOR_BUFFER:
		return -EBUSY;
	case ALLEGRO_STATE_STOPPED:
		allegro_set_state(channel, ALLEGRO_STATE_ENCODING);
		break;
	default:
		return 0;
	}

	return 0;
}

static int allegro_encoder_cmd(struct file *file, void *fh,
			       struct v4l2_encoder_cmd *cmd)
{
	struct allegro_channel *channel = fh_to_channel(fh);
	int err;

	err = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, cmd);
	if (err)
		return err;

	switch (cmd->cmd) {
	case V4L2_ENC_CMD_STOP:
		err = allegro_channel_cmd_stop(channel);
		break;
	case V4L2_ENC_CMD_START:
		err = allegro_channel_cmd_start(channel);
		break;
	default:
		err = -EINVAL;
		break;
	}

	return err;
}

static int allegro_enum_framesizes(struct file *file, void *fh,
				   struct v4l2_frmsizeenum *fsize)
{
	switch (fsize->pixel_format) {
	case V4L2_PIX_FMT_H264:
	case V4L2_PIX_FMT_NV12:
		break;
	default:
		return -EINVAL;
	}

	if (fsize->index)
		return -EINVAL;

	fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
	fsize->stepwise.min_width = ALLEGRO_WIDTH_MIN;
	fsize->stepwise.max_width = ALLEGRO_WIDTH_MAX;
	fsize->stepwise.step_width = 1;
	fsize->stepwise.min_height = ALLEGRO_HEIGHT_MIN;
	fsize->stepwise.max_height = ALLEGRO_HEIGHT_MAX;
	fsize->stepwise.step_height = 1;

	return 0;
}

static int allegro_ioctl_streamon(struct file *file, void *priv,
				  enum v4l2_buf_type type)
{
	struct v4l2_fh *fh = file->private_data;
	struct allegro_channel *channel = fh_to_channel(fh);
	int err;

	if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
		err = allegro_create_channel(channel);
		if (err)
			return err;
	}

	return v4l2_m2m_streamon(file, fh->m2m_ctx, type);
}

static int allegro_g_parm(struct file *file, void *fh,
			  struct v4l2_streamparm *a)
{
	struct allegro_channel *channel = fh_to_channel(fh);
	struct v4l2_fract *timeperframe;

	if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
		return -EINVAL;

	a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
	timeperframe = &a->parm.output.timeperframe;
	timeperframe->numerator = channel->framerate.denominator;
	timeperframe->denominator = channel->framerate.numerator;

	return 0;
}

static int allegro_s_parm(struct file *file, void *fh,
			  struct v4l2_streamparm *a)
{
	struct allegro_channel *channel = fh_to_channel(fh);
	struct v4l2_fract *timeperframe;
	int div;

	if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
		return -EINVAL;

	a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
	timeperframe = &a->parm.output.timeperframe;

	if (timeperframe->numerator == 0 || timeperframe->denominator == 0)
		return allegro_g_parm(file, fh, a);

	div = gcd(timeperframe->denominator, timeperframe->numerator);
	channel->framerate.numerator = timeperframe->denominator / div;
	channel->framerate.denominator = timeperframe->numerator / div;

	return 0;
}

static int allegro_subscribe_event(struct v4l2_fh *fh,
				   const struct v4l2_event_subscription *sub)
{
	switch (sub->type) {
	case V4L2_EVENT_EOS:
		return v4l2_event_subscribe(fh, sub, 0, NULL);
	default:
		return v4l2_ctrl_subscribe_event(fh, sub);
	}
}

static const struct v4l2_ioctl_ops allegro_ioctl_ops = {
	.vidioc_querycap = allegro_querycap,
	.vidioc_enum_fmt_vid_cap = allegro_enum_fmt_vid,
	.vidioc_enum_fmt_vid_out = allegro_enum_fmt_vid,
	.vidioc_g_fmt_vid_cap = allegro_g_fmt_vid_cap,
	.vidioc_try_fmt_vid_cap = allegro_try_fmt_vid_cap,
	.vidioc_s_fmt_vid_cap = allegro_try_fmt_vid_cap,
	.vidioc_g_fmt_vid_out = allegro_g_fmt_vid_out,
	.vidioc_try_fmt_vid_out = allegro_try_fmt_vid_out,
	.vidioc_s_fmt_vid_out = allegro_s_fmt_vid_out,

	.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
	.vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,

	.vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
	.vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
	.vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
	.vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
	.vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,

	.vidioc_streamon = allegro_ioctl_streamon,
	.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,

	.vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd,
	.vidioc_encoder_cmd = allegro_encoder_cmd,
	.vidioc_enum_framesizes = allegro_enum_framesizes,

	.vidioc_g_parm		= allegro_g_parm,
	.vidioc_s_parm		= allegro_s_parm,

	.vidioc_subscribe_event = allegro_subscribe_event,
	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};

static const struct v4l2_file_operations allegro_fops = {
	.owner = THIS_MODULE,
	.open = allegro_open,
	.release = allegro_release,
	.poll = v4l2_m2m_fop_poll,
	.unlocked_ioctl = video_ioctl2,
	.mmap = v4l2_m2m_fop_mmap,
};

static int allegro_register_device(struct allegro_dev *dev)
{
	struct video_device *video_dev = &dev->video_dev;

	strscpy(video_dev->name, "allegro", sizeof(video_dev->name));
	video_dev->fops = &allegro_fops;
	video_dev->ioctl_ops = &allegro_ioctl_ops;
	video_dev->release = video_device_release_empty;
	video_dev->lock = &dev->lock;
	video_dev->v4l2_dev = &dev->v4l2_dev;
	video_dev->vfl_dir = VFL_DIR_M2M;
	video_dev->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
	video_set_drvdata(video_dev, dev);

	return video_register_device(video_dev, VFL_TYPE_VIDEO, 0);
}

static void allegro_device_run(void *priv)
{
	struct allegro_channel *channel = priv;
	struct allegro_dev *dev = channel->dev;
	struct vb2_v4l2_buffer *src_buf;
	struct vb2_v4l2_buffer *dst_buf;
	dma_addr_t src_y;
	dma_addr_t src_uv;
	dma_addr_t dst_addr;
	unsigned long dst_size;
	u64 src_handle;
	u64 dst_handle;

	dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx);
	dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
	dst_size = vb2_plane_size(&dst_buf->vb2_buf, 0);
	dst_handle = allegro_put_buffer(channel, &channel->stream_shadow_list,
					dst_buf);
	allegro_mcu_send_put_stream_buffer(dev, channel, dst_addr, dst_size,
					   dst_handle);

	src_buf = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx);
	src_buf->sequence = channel->osequence++;
	src_y = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
	src_uv = src_y + (channel->stride * channel->height);
	src_handle = allegro_put_buffer(channel, &channel->source_shadow_list,
					src_buf);
	allegro_mcu_send_encode_frame(dev, channel, src_y, src_uv, src_handle);

	v4l2_m2m_job_finish(dev->m2m_dev, channel->fh.m2m_ctx);
}

static const struct v4l2_m2m_ops allegro_m2m_ops = {
	.device_run = allegro_device_run,
};

static int allegro_mcu_hw_init(struct allegro_dev *dev,
			       const struct fw_info *info)
{
	int err;

	allegro_mbox_init(dev, &dev->mbox_command,
			  info->mailbox_cmd, info->mailbox_size);
	allegro_mbox_init(dev, &dev->mbox_status,
			  info->mailbox_status, info->mailbox_size);

	allegro_mcu_enable_interrupts(dev);

	/* The mcu sends INIT after reset. */
	allegro_mcu_start(dev);
	err = allegro_mcu_wait_for_init_timeout(dev, 5000);
	if (err < 0) {
		v4l2_err(&dev->v4l2_dev,
			 "mcu did not send INIT after reset\n");
		err = -EIO;
		goto err_disable_interrupts;
	}

	err = allegro_alloc_buffer(dev, &dev->suballocator,
				   info->suballocator_size);
	if (err) {
		v4l2_err(&dev->v4l2_dev,
			 "failed to allocate %zu bytes for suballocator\n",
			 info->suballocator_size);
		goto err_reset_mcu;
	}

	allegro_mcu_send_init(dev, dev->suballocator.paddr,
			      dev->suballocator.size);
	err = allegro_mcu_wait_for_init_timeout(dev, 5000);
	if (err < 0) {
		v4l2_err(&dev->v4l2_dev,
			 "mcu failed to configure sub-allocator\n");
		err = -EIO;
		goto err_free_suballocator;
	}

	return 0;

err_free_suballocator:
	allegro_free_buffer(dev, &dev->suballocator);
err_reset_mcu:
	allegro_mcu_reset(dev);
err_disable_interrupts:
	allegro_mcu_disable_interrupts(dev);

	return err;
}

static int allegro_mcu_hw_deinit(struct allegro_dev *dev)
{
	int err;

	err = allegro_mcu_reset(dev);
	if (err)
		v4l2_warn(&dev->v4l2_dev,
			  "mcu failed to enter sleep state\n");

	err = allegro_mcu_disable_interrupts(dev);
	if (err)
		v4l2_warn(&dev->v4l2_dev,
			  "failed to disable interrupts\n");

	allegro_free_buffer(dev, &dev->suballocator);

	return 0;
}

static void allegro_fw_callback(const struct firmware *fw, void *context)
{
	struct allegro_dev *dev = context;
	const char *fw_codec_name = "al5e.fw";
	const struct firmware *fw_codec;
	int err;
	const struct fw_info *info;

	if (!fw)
		return;

	v4l2_dbg(1, debug, &dev->v4l2_dev,
		 "requesting codec firmware '%s'\n", fw_codec_name);
	err = request_firmware(&fw_codec, fw_codec_name, &dev->plat_dev->dev);
	if (err)
		goto err_release_firmware;

	info = allegro_get_firmware_info(dev, fw, fw_codec);
	if (!info) {
		v4l2_err(&dev->v4l2_dev, "firmware is not supported\n");
		goto err_release_firmware_codec;
	}

	v4l2_info(&dev->v4l2_dev,
		  "using mcu firmware version '%s'\n", info->version);

	/* Ensure that the mcu is sleeping at the reset vector */
	err = allegro_mcu_reset(dev);
	if (err) {
		v4l2_err(&dev->v4l2_dev, "failed to reset mcu\n");
		goto err_release_firmware_codec;
	}

	allegro_copy_firmware(dev, fw->data, fw->size);
	allegro_copy_fw_codec(dev, fw_codec->data, fw_codec->size);

	err = allegro_mcu_hw_init(dev, info);
	if (err) {
		v4l2_err(&dev->v4l2_dev, "failed to initialize mcu\n");
		goto err_free_fw_codec;
	}

	dev->m2m_dev = v4l2_m2m_init(&allegro_m2m_ops);
	if (IS_ERR(dev->m2m_dev)) {
		v4l2_err(&dev->v4l2_dev, "failed to init mem2mem device\n");
		goto err_mcu_hw_deinit;
	}

	err = allegro_register_device(dev);
	if (err) {
		v4l2_err(&dev->v4l2_dev, "failed to register video device\n");
		goto err_m2m_release;
	}

	v4l2_dbg(1, debug, &dev->v4l2_dev,
		 "allegro codec registered as /dev/video%d\n",
		 dev->video_dev.num);

	release_firmware(fw_codec);
	release_firmware(fw);

	return;

err_m2m_release:
	v4l2_m2m_release(dev->m2m_dev);
	dev->m2m_dev = NULL;
err_mcu_hw_deinit:
	allegro_mcu_hw_deinit(dev);
err_free_fw_codec:
	allegro_free_fw_codec(dev);
err_release_firmware_codec:
	release_firmware(fw_codec);
err_release_firmware:
	release_firmware(fw);
}

static int allegro_firmware_request_nowait(struct allegro_dev *dev)
{
	const char *fw = "al5e_b.fw";

	v4l2_dbg(1, debug, &dev->v4l2_dev,
		 "requesting firmware '%s'\n", fw);
	return request_firmware_nowait(THIS_MODULE, true, fw,
				       &dev->plat_dev->dev, GFP_KERNEL, dev,
				       allegro_fw_callback);
}

static int allegro_probe(struct platform_device *pdev)
{
	struct allegro_dev *dev;
	struct resource *res, *sram_res;
	int ret;
	int irq;
	void __iomem *regs, *sram_regs;

	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
	if (!dev)
		return -ENOMEM;
	dev->plat_dev = pdev;
	init_completion(&dev->init_complete);
	INIT_LIST_HEAD(&dev->channels);

	mutex_init(&dev->lock);

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
	if (!res) {
		dev_err(&pdev->dev,
			"regs resource missing from device tree\n");
		return -EINVAL;
	}
	regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
	if (IS_ERR(regs)) {
		dev_err(&pdev->dev, "failed to map registers\n");
		return PTR_ERR(regs);
	}
	dev->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
					    &allegro_regmap_config);
	if (IS_ERR(dev->regmap)) {
		dev_err(&pdev->dev, "failed to init regmap\n");
		return PTR_ERR(dev->regmap);
	}

	sram_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram");
	if (!sram_res) {
		dev_err(&pdev->dev,
			"sram resource missing from device tree\n");
		return -EINVAL;
	}
	sram_regs = devm_ioremap(&pdev->dev,
				 sram_res->start,
				 resource_size(sram_res));
	if (IS_ERR(sram_regs)) {
		dev_err(&pdev->dev, "failed to map sram\n");
		return PTR_ERR(sram_regs);
	}
	dev->sram = devm_regmap_init_mmio(&pdev->dev, sram_regs,
					  &allegro_sram_config);
	if (IS_ERR(dev->sram)) {
		dev_err(&pdev->dev, "failed to init sram\n");
		return PTR_ERR(dev->sram);
	}

	irq = platform_get_irq(pdev, 0);
	if (irq < 0)
		return irq;
	ret = devm_request_threaded_irq(&pdev->dev, irq,
					allegro_hardirq,
					allegro_irq_thread,
					IRQF_SHARED, dev_name(&pdev->dev), dev);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to request irq: %d\n", ret);
		return ret;
	}

	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
	if (ret)
		return ret;

	platform_set_drvdata(pdev, dev);

	ret = allegro_firmware_request_nowait(dev);
	if (ret < 0) {
		v4l2_err(&dev->v4l2_dev,
			 "failed to request firmware: %d\n", ret);
		return ret;
	}

	return 0;
}

static int allegro_remove(struct platform_device *pdev)
{
	struct allegro_dev *dev = platform_get_drvdata(pdev);

	video_unregister_device(&dev->video_dev);
	if (dev->m2m_dev)
		v4l2_m2m_release(dev->m2m_dev);
	allegro_mcu_hw_deinit(dev);
	allegro_free_fw_codec(dev);

	v4l2_device_unregister(&dev->v4l2_dev);

	return 0;
}

static const struct of_device_id allegro_dt_ids[] = {
	{ .compatible = "allegro,al5e-1.1" },
	{ /* sentinel */ }
};

MODULE_DEVICE_TABLE(of, allegro_dt_ids);

static struct platform_driver allegro_driver = {
	.probe = allegro_probe,
	.remove = allegro_remove,
	.driver = {
		.name = "allegro",
		.of_match_table = of_match_ptr(allegro_dt_ids),
	},
};

module_platform_driver(allegro_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michael Tretter <kernel@pengutronix.de>");
MODULE_DESCRIPTION("Allegro DVT encoder driver");