189ee7f4fSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
225aee3deSMauro Carvalho Chehab /*
325aee3deSMauro Carvalho Chehab  * ngene.c: nGene PCIe bridge driver
425aee3deSMauro Carvalho Chehab  *
525aee3deSMauro Carvalho Chehab  * Copyright (C) 2005-2007 Micronas
625aee3deSMauro Carvalho Chehab  *
725aee3deSMauro Carvalho Chehab  * Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de>
825aee3deSMauro Carvalho Chehab  *                         Modifications for new nGene firmware,
925aee3deSMauro Carvalho Chehab  *                         support for EEPROM-copying,
1025aee3deSMauro Carvalho Chehab  *                         support for new dual DVB-S2 card prototype
1125aee3deSMauro Carvalho Chehab  */
1225aee3deSMauro Carvalho Chehab 
1325aee3deSMauro Carvalho Chehab #include <linux/module.h>
1425aee3deSMauro Carvalho Chehab #include <linux/init.h>
1525aee3deSMauro Carvalho Chehab #include <linux/delay.h>
1625aee3deSMauro Carvalho Chehab #include <linux/poll.h>
1725aee3deSMauro Carvalho Chehab #include <linux/io.h>
1825aee3deSMauro Carvalho Chehab #include <asm/div64.h>
1925aee3deSMauro Carvalho Chehab #include <linux/pci.h>
2025aee3deSMauro Carvalho Chehab #include <linux/timer.h>
2125aee3deSMauro Carvalho Chehab #include <linux/byteorder/generic.h>
2225aee3deSMauro Carvalho Chehab #include <linux/firmware.h>
2325aee3deSMauro Carvalho Chehab #include <linux/vmalloc.h>
2425aee3deSMauro Carvalho Chehab 
2525aee3deSMauro Carvalho Chehab #include "ngene.h"
2625aee3deSMauro Carvalho Chehab 
2725aee3deSMauro Carvalho Chehab static int one_adapter;
2825aee3deSMauro Carvalho Chehab module_param(one_adapter, int, 0444);
2925aee3deSMauro Carvalho Chehab MODULE_PARM_DESC(one_adapter, "Use only one adapter.");
3025aee3deSMauro Carvalho Chehab 
3125aee3deSMauro Carvalho Chehab static int shutdown_workaround;
3225aee3deSMauro Carvalho Chehab module_param(shutdown_workaround, int, 0644);
3325aee3deSMauro Carvalho Chehab MODULE_PARM_DESC(shutdown_workaround, "Activate workaround for shutdown problem with some chipsets.");
3425aee3deSMauro Carvalho Chehab 
3525aee3deSMauro Carvalho Chehab static int debug;
3625aee3deSMauro Carvalho Chehab module_param(debug, int, 0444);
3725aee3deSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Print debugging information.");
3825aee3deSMauro Carvalho Chehab 
3925aee3deSMauro Carvalho Chehab DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
4025aee3deSMauro Carvalho Chehab 
41c463c979SHans Verkuil #define ngwriteb(dat, adr)         writeb((dat), dev->iomem + (adr))
42c463c979SHans Verkuil #define ngwritel(dat, adr)         writel((dat), dev->iomem + (adr))
43c463c979SHans Verkuil #define ngwriteb(dat, adr)         writeb((dat), dev->iomem + (adr))
4425aee3deSMauro Carvalho Chehab #define ngreadl(adr)               readl(dev->iomem + (adr))
4525aee3deSMauro Carvalho Chehab #define ngreadb(adr)               readb(dev->iomem + (adr))
46c463c979SHans Verkuil #define ngcpyto(adr, src, count)   memcpy_toio(dev->iomem + (adr), (src), (count))
47c463c979SHans Verkuil #define ngcpyfrom(dst, adr, count) memcpy_fromio((dst), dev->iomem + (adr), (count))
4825aee3deSMauro Carvalho Chehab 
4925aee3deSMauro Carvalho Chehab /****************************************************************************/
5025aee3deSMauro Carvalho Chehab /* nGene interrupt handler **************************************************/
5125aee3deSMauro Carvalho Chehab /****************************************************************************/
5225aee3deSMauro Carvalho Chehab 
event_tasklet(struct tasklet_struct * t)536027ff6bSAllen Pais static void event_tasklet(struct tasklet_struct *t)
5425aee3deSMauro Carvalho Chehab {
556027ff6bSAllen Pais 	struct ngene *dev = from_tasklet(dev, t, event_tasklet);
5625aee3deSMauro Carvalho Chehab 
5725aee3deSMauro Carvalho Chehab 	while (dev->EventQueueReadIndex != dev->EventQueueWriteIndex) {
5825aee3deSMauro Carvalho Chehab 		struct EVENT_BUFFER Event =
5925aee3deSMauro Carvalho Chehab 			dev->EventQueue[dev->EventQueueReadIndex];
6025aee3deSMauro Carvalho Chehab 		dev->EventQueueReadIndex =
6125aee3deSMauro Carvalho Chehab 			(dev->EventQueueReadIndex + 1) & (EVENT_QUEUE_SIZE - 1);
6225aee3deSMauro Carvalho Chehab 
6325aee3deSMauro Carvalho Chehab 		if ((Event.UARTStatus & 0x01) && (dev->TxEventNotify))
6425aee3deSMauro Carvalho Chehab 			dev->TxEventNotify(dev, Event.TimeStamp);
6525aee3deSMauro Carvalho Chehab 		if ((Event.UARTStatus & 0x02) && (dev->RxEventNotify))
6625aee3deSMauro Carvalho Chehab 			dev->RxEventNotify(dev, Event.TimeStamp,
6725aee3deSMauro Carvalho Chehab 					   Event.RXCharacter);
6825aee3deSMauro Carvalho Chehab 	}
6925aee3deSMauro Carvalho Chehab }
7025aee3deSMauro Carvalho Chehab 
demux_tasklet(struct tasklet_struct * t)716027ff6bSAllen Pais static void demux_tasklet(struct tasklet_struct *t)
7225aee3deSMauro Carvalho Chehab {
736027ff6bSAllen Pais 	struct ngene_channel *chan = from_tasklet(chan, t, demux_tasklet);
746795bf62SDaniel Scheller 	struct device *pdev = &chan->dev->pci_dev->dev;
7525aee3deSMauro Carvalho Chehab 	struct SBufferHeader *Cur = chan->nextBuffer;
7625aee3deSMauro Carvalho Chehab 
7725aee3deSMauro Carvalho Chehab 	spin_lock_irq(&chan->state_lock);
7825aee3deSMauro Carvalho Chehab 
7925aee3deSMauro Carvalho Chehab 	while (Cur->ngeneBuffer.SR.Flags & 0x80) {
8025aee3deSMauro Carvalho Chehab 		if (chan->mode & NGENE_IO_TSOUT) {
8125aee3deSMauro Carvalho Chehab 			u32 Flags = chan->DataFormatFlags;
8225aee3deSMauro Carvalho Chehab 			if (Cur->ngeneBuffer.SR.Flags & 0x20)
8325aee3deSMauro Carvalho Chehab 				Flags |= BEF_OVERFLOW;
8425aee3deSMauro Carvalho Chehab 			if (chan->pBufferExchange) {
8525aee3deSMauro Carvalho Chehab 				if (!chan->pBufferExchange(chan,
8625aee3deSMauro Carvalho Chehab 							   Cur->Buffer1,
8725aee3deSMauro Carvalho Chehab 							   chan->Capture1Length,
8825aee3deSMauro Carvalho Chehab 							   Cur->ngeneBuffer.SR.
8925aee3deSMauro Carvalho Chehab 							   Clock, Flags)) {
9025aee3deSMauro Carvalho Chehab 					/*
9125aee3deSMauro Carvalho Chehab 					   We didn't get data
9225aee3deSMauro Carvalho Chehab 					   Clear in service flag to make sure we
9325aee3deSMauro Carvalho Chehab 					   get called on next interrupt again.
9425aee3deSMauro Carvalho Chehab 					   leave fill/empty (0x80) flag alone
9525aee3deSMauro Carvalho Chehab 					   to avoid hardware running out of
9625aee3deSMauro Carvalho Chehab 					   buffers during startup, we hold only
9725aee3deSMauro Carvalho Chehab 					   in run state ( the source may be late
9825aee3deSMauro Carvalho Chehab 					   delivering data )
9925aee3deSMauro Carvalho Chehab 					*/
10025aee3deSMauro Carvalho Chehab 
10125aee3deSMauro Carvalho Chehab 					if (chan->HWState == HWSTATE_RUN) {
10225aee3deSMauro Carvalho Chehab 						Cur->ngeneBuffer.SR.Flags &=
10325aee3deSMauro Carvalho Chehab 							~0x40;
10425aee3deSMauro Carvalho Chehab 						break;
10525aee3deSMauro Carvalho Chehab 						/* Stop processing stream */
10625aee3deSMauro Carvalho Chehab 					}
10725aee3deSMauro Carvalho Chehab 				} else {
10825aee3deSMauro Carvalho Chehab 					/* We got a valid buffer,
10925aee3deSMauro Carvalho Chehab 					   so switch to run state */
11025aee3deSMauro Carvalho Chehab 					chan->HWState = HWSTATE_RUN;
11125aee3deSMauro Carvalho Chehab 				}
11225aee3deSMauro Carvalho Chehab 			} else {
1136795bf62SDaniel Scheller 				dev_err(pdev, "OOPS\n");
11425aee3deSMauro Carvalho Chehab 				if (chan->HWState == HWSTATE_RUN) {
11525aee3deSMauro Carvalho Chehab 					Cur->ngeneBuffer.SR.Flags &= ~0x40;
11625aee3deSMauro Carvalho Chehab 					break;	/* Stop processing stream */
11725aee3deSMauro Carvalho Chehab 				}
11825aee3deSMauro Carvalho Chehab 			}
11925aee3deSMauro Carvalho Chehab 			if (chan->AudioDTOUpdated) {
1206795bf62SDaniel Scheller 				dev_info(pdev, "Update AudioDTO = %d\n",
12125aee3deSMauro Carvalho Chehab 					 chan->AudioDTOValue);
12225aee3deSMauro Carvalho Chehab 				Cur->ngeneBuffer.SR.DTOUpdate =
12325aee3deSMauro Carvalho Chehab 					chan->AudioDTOValue;
12425aee3deSMauro Carvalho Chehab 				chan->AudioDTOUpdated = 0;
12525aee3deSMauro Carvalho Chehab 			}
12625aee3deSMauro Carvalho Chehab 		} else {
12725aee3deSMauro Carvalho Chehab 			if (chan->HWState == HWSTATE_RUN) {
12825aee3deSMauro Carvalho Chehab 				u32 Flags = chan->DataFormatFlags;
12925aee3deSMauro Carvalho Chehab 				IBufferExchange *exch1 = chan->pBufferExchange;
13025aee3deSMauro Carvalho Chehab 				IBufferExchange *exch2 = chan->pBufferExchange2;
13125aee3deSMauro Carvalho Chehab 				if (Cur->ngeneBuffer.SR.Flags & 0x01)
13225aee3deSMauro Carvalho Chehab 					Flags |= BEF_EVEN_FIELD;
13325aee3deSMauro Carvalho Chehab 				if (Cur->ngeneBuffer.SR.Flags & 0x20)
13425aee3deSMauro Carvalho Chehab 					Flags |= BEF_OVERFLOW;
13525aee3deSMauro Carvalho Chehab 				spin_unlock_irq(&chan->state_lock);
13625aee3deSMauro Carvalho Chehab 				if (exch1)
13725aee3deSMauro Carvalho Chehab 					exch1(chan, Cur->Buffer1,
13825aee3deSMauro Carvalho Chehab 						chan->Capture1Length,
13925aee3deSMauro Carvalho Chehab 						Cur->ngeneBuffer.SR.Clock,
14025aee3deSMauro Carvalho Chehab 						Flags);
14125aee3deSMauro Carvalho Chehab 				if (exch2)
14225aee3deSMauro Carvalho Chehab 					exch2(chan, Cur->Buffer2,
14325aee3deSMauro Carvalho Chehab 						chan->Capture2Length,
14425aee3deSMauro Carvalho Chehab 						Cur->ngeneBuffer.SR.Clock,
14525aee3deSMauro Carvalho Chehab 						Flags);
14625aee3deSMauro Carvalho Chehab 				spin_lock_irq(&chan->state_lock);
14725aee3deSMauro Carvalho Chehab 			} else if (chan->HWState != HWSTATE_STOP)
14825aee3deSMauro Carvalho Chehab 				chan->HWState = HWSTATE_RUN;
14925aee3deSMauro Carvalho Chehab 		}
15025aee3deSMauro Carvalho Chehab 		Cur->ngeneBuffer.SR.Flags = 0x00;
15125aee3deSMauro Carvalho Chehab 		Cur = Cur->Next;
15225aee3deSMauro Carvalho Chehab 	}
15325aee3deSMauro Carvalho Chehab 	chan->nextBuffer = Cur;
15425aee3deSMauro Carvalho Chehab 
15525aee3deSMauro Carvalho Chehab 	spin_unlock_irq(&chan->state_lock);
15625aee3deSMauro Carvalho Chehab }
15725aee3deSMauro Carvalho Chehab 
irq_handler(int irq,void * dev_id)15825aee3deSMauro Carvalho Chehab static irqreturn_t irq_handler(int irq, void *dev_id)
15925aee3deSMauro Carvalho Chehab {
16025aee3deSMauro Carvalho Chehab 	struct ngene *dev = (struct ngene *)dev_id;
1616795bf62SDaniel Scheller 	struct device *pdev = &dev->pci_dev->dev;
16225aee3deSMauro Carvalho Chehab 	u32 icounts = 0;
16325aee3deSMauro Carvalho Chehab 	irqreturn_t rc = IRQ_NONE;
16425aee3deSMauro Carvalho Chehab 	u32 i = MAX_STREAM;
16525aee3deSMauro Carvalho Chehab 	u8 *tmpCmdDoneByte;
16625aee3deSMauro Carvalho Chehab 
16725aee3deSMauro Carvalho Chehab 	if (dev->BootFirmware) {
16825aee3deSMauro Carvalho Chehab 		icounts = ngreadl(NGENE_INT_COUNTS);
16925aee3deSMauro Carvalho Chehab 		if (icounts != dev->icounts) {
17025aee3deSMauro Carvalho Chehab 			ngwritel(0, FORCE_NMI);
17125aee3deSMauro Carvalho Chehab 			dev->cmd_done = 1;
17225aee3deSMauro Carvalho Chehab 			wake_up(&dev->cmd_wq);
17325aee3deSMauro Carvalho Chehab 			dev->icounts = icounts;
17425aee3deSMauro Carvalho Chehab 			rc = IRQ_HANDLED;
17525aee3deSMauro Carvalho Chehab 		}
17625aee3deSMauro Carvalho Chehab 		return rc;
17725aee3deSMauro Carvalho Chehab 	}
17825aee3deSMauro Carvalho Chehab 
17925aee3deSMauro Carvalho Chehab 	ngwritel(0, FORCE_NMI);
18025aee3deSMauro Carvalho Chehab 
18125aee3deSMauro Carvalho Chehab 	spin_lock(&dev->cmd_lock);
18225aee3deSMauro Carvalho Chehab 	tmpCmdDoneByte = dev->CmdDoneByte;
18325aee3deSMauro Carvalho Chehab 	if (tmpCmdDoneByte &&
18425aee3deSMauro Carvalho Chehab 	    (*tmpCmdDoneByte ||
18525aee3deSMauro Carvalho Chehab 	    (dev->ngenetohost[0] == 1 && dev->ngenetohost[1] != 0))) {
18625aee3deSMauro Carvalho Chehab 		dev->CmdDoneByte = NULL;
18725aee3deSMauro Carvalho Chehab 		dev->cmd_done = 1;
18825aee3deSMauro Carvalho Chehab 		wake_up(&dev->cmd_wq);
18925aee3deSMauro Carvalho Chehab 		rc = IRQ_HANDLED;
19025aee3deSMauro Carvalho Chehab 	}
19125aee3deSMauro Carvalho Chehab 	spin_unlock(&dev->cmd_lock);
19225aee3deSMauro Carvalho Chehab 
19325aee3deSMauro Carvalho Chehab 	if (dev->EventBuffer->EventStatus & 0x80) {
19425aee3deSMauro Carvalho Chehab 		u8 nextWriteIndex =
19525aee3deSMauro Carvalho Chehab 			(dev->EventQueueWriteIndex + 1) &
19625aee3deSMauro Carvalho Chehab 			(EVENT_QUEUE_SIZE - 1);
19725aee3deSMauro Carvalho Chehab 		if (nextWriteIndex != dev->EventQueueReadIndex) {
19825aee3deSMauro Carvalho Chehab 			dev->EventQueue[dev->EventQueueWriteIndex] =
19925aee3deSMauro Carvalho Chehab 				*(dev->EventBuffer);
20025aee3deSMauro Carvalho Chehab 			dev->EventQueueWriteIndex = nextWriteIndex;
20125aee3deSMauro Carvalho Chehab 		} else {
2026795bf62SDaniel Scheller 			dev_err(pdev, "event overflow\n");
20325aee3deSMauro Carvalho Chehab 			dev->EventQueueOverflowCount += 1;
20425aee3deSMauro Carvalho Chehab 			dev->EventQueueOverflowFlag = 1;
20525aee3deSMauro Carvalho Chehab 		}
20625aee3deSMauro Carvalho Chehab 		dev->EventBuffer->EventStatus &= ~0x80;
20725aee3deSMauro Carvalho Chehab 		tasklet_schedule(&dev->event_tasklet);
20825aee3deSMauro Carvalho Chehab 		rc = IRQ_HANDLED;
20925aee3deSMauro Carvalho Chehab 	}
21025aee3deSMauro Carvalho Chehab 
21125aee3deSMauro Carvalho Chehab 	while (i > 0) {
21225aee3deSMauro Carvalho Chehab 		i--;
21325aee3deSMauro Carvalho Chehab 		spin_lock(&dev->channel[i].state_lock);
21425aee3deSMauro Carvalho Chehab 		/* if (dev->channel[i].State>=KSSTATE_RUN) { */
21525aee3deSMauro Carvalho Chehab 		if (dev->channel[i].nextBuffer) {
21625aee3deSMauro Carvalho Chehab 			if ((dev->channel[i].nextBuffer->
21725aee3deSMauro Carvalho Chehab 			     ngeneBuffer.SR.Flags & 0xC0) == 0x80) {
21825aee3deSMauro Carvalho Chehab 				dev->channel[i].nextBuffer->
21925aee3deSMauro Carvalho Chehab 					ngeneBuffer.SR.Flags |= 0x40;
22025aee3deSMauro Carvalho Chehab 				tasklet_schedule(
22125aee3deSMauro Carvalho Chehab 					&dev->channel[i].demux_tasklet);
22225aee3deSMauro Carvalho Chehab 				rc = IRQ_HANDLED;
22325aee3deSMauro Carvalho Chehab 			}
22425aee3deSMauro Carvalho Chehab 		}
22525aee3deSMauro Carvalho Chehab 		spin_unlock(&dev->channel[i].state_lock);
22625aee3deSMauro Carvalho Chehab 	}
22725aee3deSMauro Carvalho Chehab 
22825aee3deSMauro Carvalho Chehab 	/* Request might have been processed by a previous call. */
22925aee3deSMauro Carvalho Chehab 	return IRQ_HANDLED;
23025aee3deSMauro Carvalho Chehab }
23125aee3deSMauro Carvalho Chehab 
23225aee3deSMauro Carvalho Chehab /****************************************************************************/
23325aee3deSMauro Carvalho Chehab /* nGene command interface **************************************************/
23425aee3deSMauro Carvalho Chehab /****************************************************************************/
23525aee3deSMauro Carvalho Chehab 
dump_command_io(struct ngene * dev)23625aee3deSMauro Carvalho Chehab static void dump_command_io(struct ngene *dev)
23725aee3deSMauro Carvalho Chehab {
2386795bf62SDaniel Scheller 	struct device *pdev = &dev->pci_dev->dev;
23925aee3deSMauro Carvalho Chehab 	u8 buf[8], *b;
24025aee3deSMauro Carvalho Chehab 
24125aee3deSMauro Carvalho Chehab 	ngcpyfrom(buf, HOST_TO_NGENE, 8);
2426795bf62SDaniel Scheller 	dev_err(pdev, "host_to_ngene (%04x): %*ph\n", HOST_TO_NGENE, 8, buf);
24325aee3deSMauro Carvalho Chehab 
24425aee3deSMauro Carvalho Chehab 	ngcpyfrom(buf, NGENE_TO_HOST, 8);
2456795bf62SDaniel Scheller 	dev_err(pdev, "ngene_to_host (%04x): %*ph\n", NGENE_TO_HOST, 8, buf);
24625aee3deSMauro Carvalho Chehab 
24725aee3deSMauro Carvalho Chehab 	b = dev->hosttongene;
2486795bf62SDaniel Scheller 	dev_err(pdev, "dev->hosttongene (%p): %*ph\n", b, 8, b);
24925aee3deSMauro Carvalho Chehab 
25025aee3deSMauro Carvalho Chehab 	b = dev->ngenetohost;
2516795bf62SDaniel Scheller 	dev_err(pdev, "dev->ngenetohost (%p): %*ph\n", b, 8, b);
25225aee3deSMauro Carvalho Chehab }
25325aee3deSMauro Carvalho Chehab 
ngene_command_mutex(struct ngene * dev,struct ngene_command * com)25425aee3deSMauro Carvalho Chehab static int ngene_command_mutex(struct ngene *dev, struct ngene_command *com)
25525aee3deSMauro Carvalho Chehab {
2566795bf62SDaniel Scheller 	struct device *pdev = &dev->pci_dev->dev;
25725aee3deSMauro Carvalho Chehab 	int ret;
25825aee3deSMauro Carvalho Chehab 	u8 *tmpCmdDoneByte;
25925aee3deSMauro Carvalho Chehab 
26025aee3deSMauro Carvalho Chehab 	dev->cmd_done = 0;
26125aee3deSMauro Carvalho Chehab 
26225aee3deSMauro Carvalho Chehab 	if (com->cmd.hdr.Opcode == CMD_FWLOAD_PREPARE) {
26325aee3deSMauro Carvalho Chehab 		dev->BootFirmware = 1;
26425aee3deSMauro Carvalho Chehab 		dev->icounts = ngreadl(NGENE_INT_COUNTS);
26525aee3deSMauro Carvalho Chehab 		ngwritel(0, NGENE_COMMAND);
26625aee3deSMauro Carvalho Chehab 		ngwritel(0, NGENE_COMMAND_HI);
26725aee3deSMauro Carvalho Chehab 		ngwritel(0, NGENE_STATUS);
26825aee3deSMauro Carvalho Chehab 		ngwritel(0, NGENE_STATUS_HI);
26925aee3deSMauro Carvalho Chehab 		ngwritel(0, NGENE_EVENT);
27025aee3deSMauro Carvalho Chehab 		ngwritel(0, NGENE_EVENT_HI);
27125aee3deSMauro Carvalho Chehab 	} else if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH) {
27225aee3deSMauro Carvalho Chehab 		u64 fwio = dev->PAFWInterfaceBuffer;
27325aee3deSMauro Carvalho Chehab 
27425aee3deSMauro Carvalho Chehab 		ngwritel(fwio & 0xffffffff, NGENE_COMMAND);
27525aee3deSMauro Carvalho Chehab 		ngwritel(fwio >> 32, NGENE_COMMAND_HI);
27625aee3deSMauro Carvalho Chehab 		ngwritel((fwio + 256) & 0xffffffff, NGENE_STATUS);
27725aee3deSMauro Carvalho Chehab 		ngwritel((fwio + 256) >> 32, NGENE_STATUS_HI);
27825aee3deSMauro Carvalho Chehab 		ngwritel((fwio + 512) & 0xffffffff, NGENE_EVENT);
27925aee3deSMauro Carvalho Chehab 		ngwritel((fwio + 512) >> 32, NGENE_EVENT_HI);
28025aee3deSMauro Carvalho Chehab 	}
28125aee3deSMauro Carvalho Chehab 
28225aee3deSMauro Carvalho Chehab 	memcpy(dev->FWInterfaceBuffer, com->cmd.raw8, com->in_len + 2);
28325aee3deSMauro Carvalho Chehab 
28425aee3deSMauro Carvalho Chehab 	if (dev->BootFirmware)
28525aee3deSMauro Carvalho Chehab 		ngcpyto(HOST_TO_NGENE, com->cmd.raw8, com->in_len + 2);
28625aee3deSMauro Carvalho Chehab 
28725aee3deSMauro Carvalho Chehab 	spin_lock_irq(&dev->cmd_lock);
28825aee3deSMauro Carvalho Chehab 	tmpCmdDoneByte = dev->ngenetohost + com->out_len;
28925aee3deSMauro Carvalho Chehab 	if (!com->out_len)
29025aee3deSMauro Carvalho Chehab 		tmpCmdDoneByte++;
29125aee3deSMauro Carvalho Chehab 	*tmpCmdDoneByte = 0;
29225aee3deSMauro Carvalho Chehab 	dev->ngenetohost[0] = 0;
29325aee3deSMauro Carvalho Chehab 	dev->ngenetohost[1] = 0;
29425aee3deSMauro Carvalho Chehab 	dev->CmdDoneByte = tmpCmdDoneByte;
29525aee3deSMauro Carvalho Chehab 	spin_unlock_irq(&dev->cmd_lock);
29625aee3deSMauro Carvalho Chehab 
29725aee3deSMauro Carvalho Chehab 	/* Notify 8051. */
29825aee3deSMauro Carvalho Chehab 	ngwritel(1, FORCE_INT);
29925aee3deSMauro Carvalho Chehab 
30025aee3deSMauro Carvalho Chehab 	ret = wait_event_timeout(dev->cmd_wq, dev->cmd_done == 1, 2 * HZ);
30125aee3deSMauro Carvalho Chehab 	if (!ret) {
30225aee3deSMauro Carvalho Chehab 		/*ngwritel(0, FORCE_NMI);*/
30325aee3deSMauro Carvalho Chehab 
3046795bf62SDaniel Scheller 		dev_err(pdev, "Command timeout cmd=%02x prev=%02x\n",
30525aee3deSMauro Carvalho Chehab 			com->cmd.hdr.Opcode, dev->prev_cmd);
30625aee3deSMauro Carvalho Chehab 		dump_command_io(dev);
30725aee3deSMauro Carvalho Chehab 		return -1;
30825aee3deSMauro Carvalho Chehab 	}
30925aee3deSMauro Carvalho Chehab 	if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH)
31025aee3deSMauro Carvalho Chehab 		dev->BootFirmware = 0;
31125aee3deSMauro Carvalho Chehab 
31225aee3deSMauro Carvalho Chehab 	dev->prev_cmd = com->cmd.hdr.Opcode;
31325aee3deSMauro Carvalho Chehab 
31425aee3deSMauro Carvalho Chehab 	if (!com->out_len)
31525aee3deSMauro Carvalho Chehab 		return 0;
31625aee3deSMauro Carvalho Chehab 
31725aee3deSMauro Carvalho Chehab 	memcpy(com->cmd.raw8, dev->ngenetohost, com->out_len);
31825aee3deSMauro Carvalho Chehab 
31925aee3deSMauro Carvalho Chehab 	return 0;
32025aee3deSMauro Carvalho Chehab }
32125aee3deSMauro Carvalho Chehab 
ngene_command(struct ngene * dev,struct ngene_command * com)32225aee3deSMauro Carvalho Chehab int ngene_command(struct ngene *dev, struct ngene_command *com)
32325aee3deSMauro Carvalho Chehab {
32425aee3deSMauro Carvalho Chehab 	int result;
32525aee3deSMauro Carvalho Chehab 
3261439cdb0SBinoy Jayan 	mutex_lock(&dev->cmd_mutex);
32725aee3deSMauro Carvalho Chehab 	result = ngene_command_mutex(dev, com);
3281439cdb0SBinoy Jayan 	mutex_unlock(&dev->cmd_mutex);
32925aee3deSMauro Carvalho Chehab 	return result;
33025aee3deSMauro Carvalho Chehab }
33125aee3deSMauro Carvalho Chehab 
33225aee3deSMauro Carvalho Chehab 
ngene_command_load_firmware(struct ngene * dev,u8 * ngene_fw,u32 size)33325aee3deSMauro Carvalho Chehab static int ngene_command_load_firmware(struct ngene *dev,
33425aee3deSMauro Carvalho Chehab 				       u8 *ngene_fw, u32 size)
33525aee3deSMauro Carvalho Chehab {
33625aee3deSMauro Carvalho Chehab #define FIRSTCHUNK (1024)
33725aee3deSMauro Carvalho Chehab 	u32 cleft;
33825aee3deSMauro Carvalho Chehab 	struct ngene_command com;
33925aee3deSMauro Carvalho Chehab 
34025aee3deSMauro Carvalho Chehab 	com.cmd.hdr.Opcode = CMD_FWLOAD_PREPARE;
34125aee3deSMauro Carvalho Chehab 	com.cmd.hdr.Length = 0;
34225aee3deSMauro Carvalho Chehab 	com.in_len = 0;
34325aee3deSMauro Carvalho Chehab 	com.out_len = 0;
34425aee3deSMauro Carvalho Chehab 
34525aee3deSMauro Carvalho Chehab 	ngene_command(dev, &com);
34625aee3deSMauro Carvalho Chehab 
34725aee3deSMauro Carvalho Chehab 	cleft = (size + 3) & ~3;
34825aee3deSMauro Carvalho Chehab 	if (cleft > FIRSTCHUNK) {
34925aee3deSMauro Carvalho Chehab 		ngcpyto(PROGRAM_SRAM + FIRSTCHUNK, ngene_fw + FIRSTCHUNK,
35025aee3deSMauro Carvalho Chehab 			cleft - FIRSTCHUNK);
35125aee3deSMauro Carvalho Chehab 		cleft = FIRSTCHUNK;
35225aee3deSMauro Carvalho Chehab 	}
35325aee3deSMauro Carvalho Chehab 	ngcpyto(DATA_FIFO_AREA, ngene_fw, cleft);
35425aee3deSMauro Carvalho Chehab 
35525aee3deSMauro Carvalho Chehab 	memset(&com, 0, sizeof(struct ngene_command));
35625aee3deSMauro Carvalho Chehab 	com.cmd.hdr.Opcode = CMD_FWLOAD_FINISH;
35725aee3deSMauro Carvalho Chehab 	com.cmd.hdr.Length = 4;
35825aee3deSMauro Carvalho Chehab 	com.cmd.FWLoadFinish.Address = DATA_FIFO_AREA;
35925aee3deSMauro Carvalho Chehab 	com.cmd.FWLoadFinish.Length = (unsigned short)cleft;
36025aee3deSMauro Carvalho Chehab 	com.in_len = 4;
36125aee3deSMauro Carvalho Chehab 	com.out_len = 0;
36225aee3deSMauro Carvalho Chehab 
36325aee3deSMauro Carvalho Chehab 	return ngene_command(dev, &com);
36425aee3deSMauro Carvalho Chehab }
36525aee3deSMauro Carvalho Chehab 
36625aee3deSMauro Carvalho Chehab 
ngene_command_config_buf(struct ngene * dev,u8 config)36725aee3deSMauro Carvalho Chehab static int ngene_command_config_buf(struct ngene *dev, u8 config)
36825aee3deSMauro Carvalho Chehab {
36925aee3deSMauro Carvalho Chehab 	struct ngene_command com;
37025aee3deSMauro Carvalho Chehab 
37125aee3deSMauro Carvalho Chehab 	com.cmd.hdr.Opcode = CMD_CONFIGURE_BUFFER;
37225aee3deSMauro Carvalho Chehab 	com.cmd.hdr.Length = 1;
37325aee3deSMauro Carvalho Chehab 	com.cmd.ConfigureBuffers.config = config;
37425aee3deSMauro Carvalho Chehab 	com.in_len = 1;
37525aee3deSMauro Carvalho Chehab 	com.out_len = 0;
37625aee3deSMauro Carvalho Chehab 
37725aee3deSMauro Carvalho Chehab 	if (ngene_command(dev, &com) < 0)
37825aee3deSMauro Carvalho Chehab 		return -EIO;
37925aee3deSMauro Carvalho Chehab 	return 0;
38025aee3deSMauro Carvalho Chehab }
38125aee3deSMauro Carvalho Chehab 
ngene_command_config_free_buf(struct ngene * dev,u8 * config)38225aee3deSMauro Carvalho Chehab static int ngene_command_config_free_buf(struct ngene *dev, u8 *config)
38325aee3deSMauro Carvalho Chehab {
38425aee3deSMauro Carvalho Chehab 	struct ngene_command com;
38525aee3deSMauro Carvalho Chehab 
38625aee3deSMauro Carvalho Chehab 	com.cmd.hdr.Opcode = CMD_CONFIGURE_FREE_BUFFER;
38725aee3deSMauro Carvalho Chehab 	com.cmd.hdr.Length = 6;
3888d4abca9SGustavo A. R. Silva 	memcpy(&com.cmd.ConfigureFreeBuffers.config, config, 6);
38925aee3deSMauro Carvalho Chehab 	com.in_len = 6;
39025aee3deSMauro Carvalho Chehab 	com.out_len = 0;
39125aee3deSMauro Carvalho Chehab 
39225aee3deSMauro Carvalho Chehab 	if (ngene_command(dev, &com) < 0)
39325aee3deSMauro Carvalho Chehab 		return -EIO;
39425aee3deSMauro Carvalho Chehab 
39525aee3deSMauro Carvalho Chehab 	return 0;
39625aee3deSMauro Carvalho Chehab }
39725aee3deSMauro Carvalho Chehab 
ngene_command_gpio_set(struct ngene * dev,u8 select,u8 level)39825aee3deSMauro Carvalho Chehab int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level)
39925aee3deSMauro Carvalho Chehab {
40025aee3deSMauro Carvalho Chehab 	struct ngene_command com;
40125aee3deSMauro Carvalho Chehab 
40225aee3deSMauro Carvalho Chehab 	com.cmd.hdr.Opcode = CMD_SET_GPIO_PIN;
40325aee3deSMauro Carvalho Chehab 	com.cmd.hdr.Length = 1;
40425aee3deSMauro Carvalho Chehab 	com.cmd.SetGpioPin.select = select | (level << 7);
40525aee3deSMauro Carvalho Chehab 	com.in_len = 1;
40625aee3deSMauro Carvalho Chehab 	com.out_len = 0;
40725aee3deSMauro Carvalho Chehab 
40825aee3deSMauro Carvalho Chehab 	return ngene_command(dev, &com);
40925aee3deSMauro Carvalho Chehab }
41025aee3deSMauro Carvalho Chehab 
41125aee3deSMauro Carvalho Chehab 
41225aee3deSMauro Carvalho Chehab /*
41325aee3deSMauro Carvalho Chehab  02000640 is sample on rising edge.
41425aee3deSMauro Carvalho Chehab  02000740 is sample on falling edge.
41525aee3deSMauro Carvalho Chehab  02000040 is ignore "valid" signal
41625aee3deSMauro Carvalho Chehab 
41725aee3deSMauro Carvalho Chehab  0: FD_CTL1 Bit 7,6 must be 0,1
41825aee3deSMauro Carvalho Chehab     7   disable(fw controlled)
41925aee3deSMauro Carvalho Chehab     6   0-AUX,1-TS
42025aee3deSMauro Carvalho Chehab     5   0-par,1-ser
42125aee3deSMauro Carvalho Chehab     4   0-lsb/1-msb
42225aee3deSMauro Carvalho Chehab     3,2 reserved
42325aee3deSMauro Carvalho Chehab     1,0 0-no sync, 1-use ext. start, 2-use 0x47, 3-both
42425aee3deSMauro Carvalho Chehab  1: FD_CTL2 has 3-valid must be hi, 2-use valid, 1-edge
42525aee3deSMauro Carvalho Chehab  2: FD_STA is read-only. 0-sync
42625aee3deSMauro Carvalho Chehab  3: FD_INSYNC is number of 47s to trigger "in sync".
42725aee3deSMauro Carvalho Chehab  4: FD_OUTSYNC is number of 47s to trigger "out of sync".
42825aee3deSMauro Carvalho Chehab  5: FD_MAXBYTE1 is low-order of bytes per packet.
42925aee3deSMauro Carvalho Chehab  6: FD_MAXBYTE2 is high-order of bytes per packet.
43025aee3deSMauro Carvalho Chehab  7: Top byte is unused.
43125aee3deSMauro Carvalho Chehab */
43225aee3deSMauro Carvalho Chehab 
43325aee3deSMauro Carvalho Chehab /****************************************************************************/
43425aee3deSMauro Carvalho Chehab 
43525aee3deSMauro Carvalho Chehab static u8 TSFeatureDecoderSetup[8 * 5] = {
43625aee3deSMauro Carvalho Chehab 	0x42, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00,
43725aee3deSMauro Carvalho Chehab 	0x40, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00,	/* DRXH */
43825aee3deSMauro Carvalho Chehab 	0x71, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00,	/* DRXHser */
43925aee3deSMauro Carvalho Chehab 	0x72, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00,	/* S2ser */
44025aee3deSMauro Carvalho Chehab 	0x40, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* LGDT3303 */
44125aee3deSMauro Carvalho Chehab };
44225aee3deSMauro Carvalho Chehab 
44325aee3deSMauro Carvalho Chehab /* Set NGENE I2S Config to 16 bit packed */
44425aee3deSMauro Carvalho Chehab static u8 I2SConfiguration[] = {
44525aee3deSMauro Carvalho Chehab 	0x00, 0x10, 0x00, 0x00,
44625aee3deSMauro Carvalho Chehab 	0x80, 0x10, 0x00, 0x00,
44725aee3deSMauro Carvalho Chehab };
44825aee3deSMauro Carvalho Chehab 
44925aee3deSMauro Carvalho Chehab static u8 SPDIFConfiguration[10] = {
45025aee3deSMauro Carvalho Chehab 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
45125aee3deSMauro Carvalho Chehab };
45225aee3deSMauro Carvalho Chehab 
45325aee3deSMauro Carvalho Chehab /* Set NGENE I2S Config to transport stream compatible mode */
45425aee3deSMauro Carvalho Chehab 
45525aee3deSMauro Carvalho Chehab static u8 TS_I2SConfiguration[4] = { 0x3E, 0x18, 0x00, 0x00 };
45625aee3deSMauro Carvalho Chehab 
45725aee3deSMauro Carvalho Chehab static u8 TS_I2SOutConfiguration[4] = { 0x80, 0x04, 0x00, 0x00 };
45825aee3deSMauro Carvalho Chehab 
45925aee3deSMauro Carvalho Chehab static u8 ITUDecoderSetup[4][16] = {
46025aee3deSMauro Carvalho Chehab 	{0x1c, 0x13, 0x01, 0x68, 0x3d, 0x90, 0x14, 0x20,  /* SDTV */
46125aee3deSMauro Carvalho Chehab 	 0x00, 0x00, 0x01, 0xb0, 0x9c, 0x00, 0x00, 0x00},
46225aee3deSMauro Carvalho Chehab 	{0x9c, 0x03, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00,
46325aee3deSMauro Carvalho Chehab 	 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00},
46425aee3deSMauro Carvalho Chehab 	{0x9f, 0x00, 0x23, 0xC0, 0x60, 0x0F, 0x13, 0x00,  /* HDTV 1080i50 */
46525aee3deSMauro Carvalho Chehab 	 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00},
46625aee3deSMauro Carvalho Chehab 	{0x9c, 0x01, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00,  /* HDTV 1080i60 */
46725aee3deSMauro Carvalho Chehab 	 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00},
46825aee3deSMauro Carvalho Chehab };
46925aee3deSMauro Carvalho Chehab 
47025aee3deSMauro Carvalho Chehab /*
47125aee3deSMauro Carvalho Chehab  * 50 48 60 gleich
47225aee3deSMauro Carvalho Chehab  * 27p50 9f 00 22 80 42 69 18 ...
47325aee3deSMauro Carvalho Chehab  * 27p60 93 00 22 80 82 69 1c ...
47425aee3deSMauro Carvalho Chehab  */
47525aee3deSMauro Carvalho Chehab 
47625aee3deSMauro Carvalho Chehab /* Maxbyte to 1144 (for raw data) */
47725aee3deSMauro Carvalho Chehab static u8 ITUFeatureDecoderSetup[8] = {
47825aee3deSMauro Carvalho Chehab 	0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x04, 0x00
47925aee3deSMauro Carvalho Chehab };
48025aee3deSMauro Carvalho Chehab 
FillTSBuffer(void * Buffer,int Length,u32 Flags)48125aee3deSMauro Carvalho Chehab void FillTSBuffer(void *Buffer, int Length, u32 Flags)
48225aee3deSMauro Carvalho Chehab {
48325aee3deSMauro Carvalho Chehab 	u32 *ptr = Buffer;
48425aee3deSMauro Carvalho Chehab 
48525aee3deSMauro Carvalho Chehab 	memset(Buffer, TS_FILLER, Length);
48625aee3deSMauro Carvalho Chehab 	while (Length > 0) {
48725aee3deSMauro Carvalho Chehab 		if (Flags & DF_SWAP32)
48825aee3deSMauro Carvalho Chehab 			*ptr = 0x471FFF10;
48925aee3deSMauro Carvalho Chehab 		else
49025aee3deSMauro Carvalho Chehab 			*ptr = 0x10FF1F47;
49125aee3deSMauro Carvalho Chehab 		ptr += (188 / 4);
49225aee3deSMauro Carvalho Chehab 		Length -= 188;
49325aee3deSMauro Carvalho Chehab 	}
49425aee3deSMauro Carvalho Chehab }
49525aee3deSMauro Carvalho Chehab 
49625aee3deSMauro Carvalho Chehab 
flush_buffers(struct ngene_channel * chan)49725aee3deSMauro Carvalho Chehab static void flush_buffers(struct ngene_channel *chan)
49825aee3deSMauro Carvalho Chehab {
49925aee3deSMauro Carvalho Chehab 	u8 val;
50025aee3deSMauro Carvalho Chehab 
50125aee3deSMauro Carvalho Chehab 	do {
50225aee3deSMauro Carvalho Chehab 		msleep(1);
50325aee3deSMauro Carvalho Chehab 		spin_lock_irq(&chan->state_lock);
50425aee3deSMauro Carvalho Chehab 		val = chan->nextBuffer->ngeneBuffer.SR.Flags & 0x80;
50525aee3deSMauro Carvalho Chehab 		spin_unlock_irq(&chan->state_lock);
50625aee3deSMauro Carvalho Chehab 	} while (val);
50725aee3deSMauro Carvalho Chehab }
50825aee3deSMauro Carvalho Chehab 
clear_buffers(struct ngene_channel * chan)50925aee3deSMauro Carvalho Chehab static void clear_buffers(struct ngene_channel *chan)
51025aee3deSMauro Carvalho Chehab {
51125aee3deSMauro Carvalho Chehab 	struct SBufferHeader *Cur = chan->nextBuffer;
51225aee3deSMauro Carvalho Chehab 
51325aee3deSMauro Carvalho Chehab 	do {
51425aee3deSMauro Carvalho Chehab 		memset(&Cur->ngeneBuffer.SR, 0, sizeof(Cur->ngeneBuffer.SR));
51525aee3deSMauro Carvalho Chehab 		if (chan->mode & NGENE_IO_TSOUT)
51625aee3deSMauro Carvalho Chehab 			FillTSBuffer(Cur->Buffer1,
51725aee3deSMauro Carvalho Chehab 				     chan->Capture1Length,
51825aee3deSMauro Carvalho Chehab 				     chan->DataFormatFlags);
51925aee3deSMauro Carvalho Chehab 		Cur = Cur->Next;
52025aee3deSMauro Carvalho Chehab 	} while (Cur != chan->nextBuffer);
52125aee3deSMauro Carvalho Chehab 
52225aee3deSMauro Carvalho Chehab 	if (chan->mode & NGENE_IO_TSOUT) {
52325aee3deSMauro Carvalho Chehab 		chan->nextBuffer->ngeneBuffer.SR.DTOUpdate =
52425aee3deSMauro Carvalho Chehab 			chan->AudioDTOValue;
52525aee3deSMauro Carvalho Chehab 		chan->AudioDTOUpdated = 0;
52625aee3deSMauro Carvalho Chehab 
52725aee3deSMauro Carvalho Chehab 		Cur = chan->TSIdleBuffer.Head;
52825aee3deSMauro Carvalho Chehab 
52925aee3deSMauro Carvalho Chehab 		do {
53025aee3deSMauro Carvalho Chehab 			memset(&Cur->ngeneBuffer.SR, 0,
53125aee3deSMauro Carvalho Chehab 			       sizeof(Cur->ngeneBuffer.SR));
53225aee3deSMauro Carvalho Chehab 			FillTSBuffer(Cur->Buffer1,
53325aee3deSMauro Carvalho Chehab 				     chan->Capture1Length,
53425aee3deSMauro Carvalho Chehab 				     chan->DataFormatFlags);
53525aee3deSMauro Carvalho Chehab 			Cur = Cur->Next;
53625aee3deSMauro Carvalho Chehab 		} while (Cur != chan->TSIdleBuffer.Head);
53725aee3deSMauro Carvalho Chehab 	}
53825aee3deSMauro Carvalho Chehab }
53925aee3deSMauro Carvalho Chehab 
ngene_command_stream_control(struct ngene * dev,u8 stream,u8 control,u8 mode,u8 flags)54025aee3deSMauro Carvalho Chehab static int ngene_command_stream_control(struct ngene *dev, u8 stream,
54125aee3deSMauro Carvalho Chehab 					u8 control, u8 mode, u8 flags)
54225aee3deSMauro Carvalho Chehab {
5436795bf62SDaniel Scheller 	struct device *pdev = &dev->pci_dev->dev;
54425aee3deSMauro Carvalho Chehab 	struct ngene_channel *chan = &dev->channel[stream];
54525aee3deSMauro Carvalho Chehab 	struct ngene_command com;
54625aee3deSMauro Carvalho Chehab 	u16 BsUVI = ((stream & 1) ? 0x9400 : 0x9300);
54725aee3deSMauro Carvalho Chehab 	u16 BsSDI = ((stream & 1) ? 0x9600 : 0x9500);
54825aee3deSMauro Carvalho Chehab 	u16 BsSPI = ((stream & 1) ? 0x9800 : 0x9700);
54925aee3deSMauro Carvalho Chehab 	u16 BsSDO = 0x9B00;
55025aee3deSMauro Carvalho Chehab 
55125aee3deSMauro Carvalho Chehab 	memset(&com, 0, sizeof(com));
55225aee3deSMauro Carvalho Chehab 	com.cmd.hdr.Opcode = CMD_CONTROL;
55325aee3deSMauro Carvalho Chehab 	com.cmd.hdr.Length = sizeof(struct FW_STREAM_CONTROL) - 2;
55425aee3deSMauro Carvalho Chehab 	com.cmd.StreamControl.Stream = stream | (control ? 8 : 0);
55525aee3deSMauro Carvalho Chehab 	if (chan->mode & NGENE_IO_TSOUT)
55625aee3deSMauro Carvalho Chehab 		com.cmd.StreamControl.Stream |= 0x07;
55725aee3deSMauro Carvalho Chehab 	com.cmd.StreamControl.Control = control |
55825aee3deSMauro Carvalho Chehab 		(flags & SFLAG_ORDER_LUMA_CHROMA);
55925aee3deSMauro Carvalho Chehab 	com.cmd.StreamControl.Mode = mode;
56025aee3deSMauro Carvalho Chehab 	com.in_len = sizeof(struct FW_STREAM_CONTROL);
56125aee3deSMauro Carvalho Chehab 	com.out_len = 0;
56225aee3deSMauro Carvalho Chehab 
5636795bf62SDaniel Scheller 	dev_dbg(pdev, "Stream=%02x, Control=%02x, Mode=%02x\n",
56425aee3deSMauro Carvalho Chehab 		com.cmd.StreamControl.Stream, com.cmd.StreamControl.Control,
56525aee3deSMauro Carvalho Chehab 		com.cmd.StreamControl.Mode);
56625aee3deSMauro Carvalho Chehab 
56725aee3deSMauro Carvalho Chehab 	chan->Mode = mode;
56825aee3deSMauro Carvalho Chehab 
56925aee3deSMauro Carvalho Chehab 	if (!(control & 0x80)) {
57025aee3deSMauro Carvalho Chehab 		spin_lock_irq(&chan->state_lock);
57125aee3deSMauro Carvalho Chehab 		if (chan->State == KSSTATE_RUN) {
57225aee3deSMauro Carvalho Chehab 			chan->State = KSSTATE_ACQUIRE;
57325aee3deSMauro Carvalho Chehab 			chan->HWState = HWSTATE_STOP;
57425aee3deSMauro Carvalho Chehab 			spin_unlock_irq(&chan->state_lock);
57590979f04SBinoy Jayan 			if (ngene_command(dev, &com) < 0)
57625aee3deSMauro Carvalho Chehab 				return -1;
57725aee3deSMauro Carvalho Chehab 			/* clear_buffers(chan); */
57825aee3deSMauro Carvalho Chehab 			flush_buffers(chan);
57925aee3deSMauro Carvalho Chehab 			return 0;
58025aee3deSMauro Carvalho Chehab 		}
58125aee3deSMauro Carvalho Chehab 		spin_unlock_irq(&chan->state_lock);
58225aee3deSMauro Carvalho Chehab 		return 0;
58325aee3deSMauro Carvalho Chehab 	}
58425aee3deSMauro Carvalho Chehab 
58525aee3deSMauro Carvalho Chehab 	if (mode & SMODE_AUDIO_CAPTURE) {
58625aee3deSMauro Carvalho Chehab 		com.cmd.StreamControl.CaptureBlockCount =
58725aee3deSMauro Carvalho Chehab 			chan->Capture1Length / AUDIO_BLOCK_SIZE;
58825aee3deSMauro Carvalho Chehab 		com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead;
58925aee3deSMauro Carvalho Chehab 	} else if (mode & SMODE_TRANSPORT_STREAM) {
59025aee3deSMauro Carvalho Chehab 		com.cmd.StreamControl.CaptureBlockCount =
59125aee3deSMauro Carvalho Chehab 			chan->Capture1Length / TS_BLOCK_SIZE;
59225aee3deSMauro Carvalho Chehab 		com.cmd.StreamControl.MaxLinesPerField =
59325aee3deSMauro Carvalho Chehab 			chan->Capture1Length / TS_BLOCK_SIZE;
59425aee3deSMauro Carvalho Chehab 		com.cmd.StreamControl.Buffer_Address =
59525aee3deSMauro Carvalho Chehab 			chan->TSRingBuffer.PAHead;
59625aee3deSMauro Carvalho Chehab 		if (chan->mode & NGENE_IO_TSOUT) {
59725aee3deSMauro Carvalho Chehab 			com.cmd.StreamControl.BytesPerVBILine =
59825aee3deSMauro Carvalho Chehab 				chan->Capture1Length / TS_BLOCK_SIZE;
59925aee3deSMauro Carvalho Chehab 			com.cmd.StreamControl.Stream |= 0x07;
60025aee3deSMauro Carvalho Chehab 		}
60125aee3deSMauro Carvalho Chehab 	} else {
60225aee3deSMauro Carvalho Chehab 		com.cmd.StreamControl.BytesPerVideoLine = chan->nBytesPerLine;
60325aee3deSMauro Carvalho Chehab 		com.cmd.StreamControl.MaxLinesPerField = chan->nLines;
60425aee3deSMauro Carvalho Chehab 		com.cmd.StreamControl.MinLinesPerField = 100;
60525aee3deSMauro Carvalho Chehab 		com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead;
60625aee3deSMauro Carvalho Chehab 
60725aee3deSMauro Carvalho Chehab 		if (mode & SMODE_VBI_CAPTURE) {
60825aee3deSMauro Carvalho Chehab 			com.cmd.StreamControl.MaxVBILinesPerField =
60925aee3deSMauro Carvalho Chehab 				chan->nVBILines;
61025aee3deSMauro Carvalho Chehab 			com.cmd.StreamControl.MinVBILinesPerField = 0;
61125aee3deSMauro Carvalho Chehab 			com.cmd.StreamControl.BytesPerVBILine =
61225aee3deSMauro Carvalho Chehab 				chan->nBytesPerVBILine;
61325aee3deSMauro Carvalho Chehab 		}
61425aee3deSMauro Carvalho Chehab 		if (flags & SFLAG_COLORBAR)
61525aee3deSMauro Carvalho Chehab 			com.cmd.StreamControl.Stream |= 0x04;
61625aee3deSMauro Carvalho Chehab 	}
61725aee3deSMauro Carvalho Chehab 
61825aee3deSMauro Carvalho Chehab 	spin_lock_irq(&chan->state_lock);
61925aee3deSMauro Carvalho Chehab 	if (mode & SMODE_AUDIO_CAPTURE) {
62025aee3deSMauro Carvalho Chehab 		chan->nextBuffer = chan->RingBuffer.Head;
62125aee3deSMauro Carvalho Chehab 		if (mode & SMODE_AUDIO_SPDIF) {
62225aee3deSMauro Carvalho Chehab 			com.cmd.StreamControl.SetupDataLen =
62325aee3deSMauro Carvalho Chehab 				sizeof(SPDIFConfiguration);
62425aee3deSMauro Carvalho Chehab 			com.cmd.StreamControl.SetupDataAddr = BsSPI;
62525aee3deSMauro Carvalho Chehab 			memcpy(com.cmd.StreamControl.SetupData,
62625aee3deSMauro Carvalho Chehab 			       SPDIFConfiguration, sizeof(SPDIFConfiguration));
62725aee3deSMauro Carvalho Chehab 		} else {
62825aee3deSMauro Carvalho Chehab 			com.cmd.StreamControl.SetupDataLen = 4;
62925aee3deSMauro Carvalho Chehab 			com.cmd.StreamControl.SetupDataAddr = BsSDI;
63025aee3deSMauro Carvalho Chehab 			memcpy(com.cmd.StreamControl.SetupData,
63125aee3deSMauro Carvalho Chehab 			       I2SConfiguration +
63225aee3deSMauro Carvalho Chehab 			       4 * dev->card_info->i2s[stream], 4);
63325aee3deSMauro Carvalho Chehab 		}
63425aee3deSMauro Carvalho Chehab 	} else if (mode & SMODE_TRANSPORT_STREAM) {
63525aee3deSMauro Carvalho Chehab 		chan->nextBuffer = chan->TSRingBuffer.Head;
63625aee3deSMauro Carvalho Chehab 		if (stream >= STREAM_AUDIOIN1) {
63725aee3deSMauro Carvalho Chehab 			if (chan->mode & NGENE_IO_TSOUT) {
63825aee3deSMauro Carvalho Chehab 				com.cmd.StreamControl.SetupDataLen =
63925aee3deSMauro Carvalho Chehab 					sizeof(TS_I2SOutConfiguration);
64025aee3deSMauro Carvalho Chehab 				com.cmd.StreamControl.SetupDataAddr = BsSDO;
64125aee3deSMauro Carvalho Chehab 				memcpy(com.cmd.StreamControl.SetupData,
64225aee3deSMauro Carvalho Chehab 				       TS_I2SOutConfiguration,
64325aee3deSMauro Carvalho Chehab 				       sizeof(TS_I2SOutConfiguration));
64425aee3deSMauro Carvalho Chehab 			} else {
64525aee3deSMauro Carvalho Chehab 				com.cmd.StreamControl.SetupDataLen =
64625aee3deSMauro Carvalho Chehab 					sizeof(TS_I2SConfiguration);
64725aee3deSMauro Carvalho Chehab 				com.cmd.StreamControl.SetupDataAddr = BsSDI;
64825aee3deSMauro Carvalho Chehab 				memcpy(com.cmd.StreamControl.SetupData,
64925aee3deSMauro Carvalho Chehab 				       TS_I2SConfiguration,
65025aee3deSMauro Carvalho Chehab 				       sizeof(TS_I2SConfiguration));
65125aee3deSMauro Carvalho Chehab 			}
65225aee3deSMauro Carvalho Chehab 		} else {
65325aee3deSMauro Carvalho Chehab 			com.cmd.StreamControl.SetupDataLen = 8;
65425aee3deSMauro Carvalho Chehab 			com.cmd.StreamControl.SetupDataAddr = BsUVI + 0x10;
65525aee3deSMauro Carvalho Chehab 			memcpy(com.cmd.StreamControl.SetupData,
65625aee3deSMauro Carvalho Chehab 			       TSFeatureDecoderSetup +
65725aee3deSMauro Carvalho Chehab 			       8 * dev->card_info->tsf[stream], 8);
65825aee3deSMauro Carvalho Chehab 		}
65925aee3deSMauro Carvalho Chehab 	} else {
66025aee3deSMauro Carvalho Chehab 		chan->nextBuffer = chan->RingBuffer.Head;
66125aee3deSMauro Carvalho Chehab 		com.cmd.StreamControl.SetupDataLen =
66225aee3deSMauro Carvalho Chehab 			16 + sizeof(ITUFeatureDecoderSetup);
66325aee3deSMauro Carvalho Chehab 		com.cmd.StreamControl.SetupDataAddr = BsUVI;
66425aee3deSMauro Carvalho Chehab 		memcpy(com.cmd.StreamControl.SetupData,
66525aee3deSMauro Carvalho Chehab 		       ITUDecoderSetup[chan->itumode], 16);
66625aee3deSMauro Carvalho Chehab 		memcpy(com.cmd.StreamControl.SetupData + 16,
66725aee3deSMauro Carvalho Chehab 		       ITUFeatureDecoderSetup, sizeof(ITUFeatureDecoderSetup));
66825aee3deSMauro Carvalho Chehab 	}
66925aee3deSMauro Carvalho Chehab 	clear_buffers(chan);
67025aee3deSMauro Carvalho Chehab 	chan->State = KSSTATE_RUN;
67125aee3deSMauro Carvalho Chehab 	if (mode & SMODE_TRANSPORT_STREAM)
67225aee3deSMauro Carvalho Chehab 		chan->HWState = HWSTATE_RUN;
67325aee3deSMauro Carvalho Chehab 	else
67425aee3deSMauro Carvalho Chehab 		chan->HWState = HWSTATE_STARTUP;
67525aee3deSMauro Carvalho Chehab 	spin_unlock_irq(&chan->state_lock);
67625aee3deSMauro Carvalho Chehab 
67790979f04SBinoy Jayan 	if (ngene_command(dev, &com) < 0)
67825aee3deSMauro Carvalho Chehab 		return -1;
67990979f04SBinoy Jayan 
68025aee3deSMauro Carvalho Chehab 	return 0;
68125aee3deSMauro Carvalho Chehab }
68225aee3deSMauro Carvalho Chehab 
set_transfer(struct ngene_channel * chan,int state)68325aee3deSMauro Carvalho Chehab void set_transfer(struct ngene_channel *chan, int state)
68425aee3deSMauro Carvalho Chehab {
6856795bf62SDaniel Scheller 	struct device *pdev = &chan->dev->pci_dev->dev;
68625aee3deSMauro Carvalho Chehab 	u8 control = 0, mode = 0, flags = 0;
68725aee3deSMauro Carvalho Chehab 	struct ngene *dev = chan->dev;
68825aee3deSMauro Carvalho Chehab 	int ret;
68925aee3deSMauro Carvalho Chehab 
69025aee3deSMauro Carvalho Chehab 	/*
6916795bf62SDaniel Scheller 	dev_info(pdev, "st %d\n", state);
69225aee3deSMauro Carvalho Chehab 	msleep(100);
69325aee3deSMauro Carvalho Chehab 	*/
69425aee3deSMauro Carvalho Chehab 
69525aee3deSMauro Carvalho Chehab 	if (state) {
69625aee3deSMauro Carvalho Chehab 		if (chan->running) {
6976795bf62SDaniel Scheller 			dev_info(pdev, "already running\n");
69825aee3deSMauro Carvalho Chehab 			return;
69925aee3deSMauro Carvalho Chehab 		}
70025aee3deSMauro Carvalho Chehab 	} else {
70125aee3deSMauro Carvalho Chehab 		if (!chan->running) {
7026795bf62SDaniel Scheller 			dev_info(pdev, "already stopped\n");
70325aee3deSMauro Carvalho Chehab 			return;
70425aee3deSMauro Carvalho Chehab 		}
70525aee3deSMauro Carvalho Chehab 	}
70625aee3deSMauro Carvalho Chehab 
70725aee3deSMauro Carvalho Chehab 	if (dev->card_info->switch_ctrl)
70825aee3deSMauro Carvalho Chehab 		dev->card_info->switch_ctrl(chan, 1, state ^ 1);
70925aee3deSMauro Carvalho Chehab 
71025aee3deSMauro Carvalho Chehab 	if (state) {
71125aee3deSMauro Carvalho Chehab 		spin_lock_irq(&chan->state_lock);
71225aee3deSMauro Carvalho Chehab 
7136795bf62SDaniel Scheller 		/* dev_info(pdev, "lock=%08x\n",
71425aee3deSMauro Carvalho Chehab 			  ngreadl(0x9310)); */
71525aee3deSMauro Carvalho Chehab 		dvb_ringbuffer_flush(&dev->tsout_rbuf);
71625aee3deSMauro Carvalho Chehab 		control = 0x80;
71725aee3deSMauro Carvalho Chehab 		if (chan->mode & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
71825aee3deSMauro Carvalho Chehab 			chan->Capture1Length = 512 * 188;
71925aee3deSMauro Carvalho Chehab 			mode = SMODE_TRANSPORT_STREAM;
72025aee3deSMauro Carvalho Chehab 		}
72125aee3deSMauro Carvalho Chehab 		if (chan->mode & NGENE_IO_TSOUT) {
72225aee3deSMauro Carvalho Chehab 			chan->pBufferExchange = tsout_exchange;
72325aee3deSMauro Carvalho Chehab 			/* 0x66666666 = 50MHz *2^33 /250MHz */
72425aee3deSMauro Carvalho Chehab 			chan->AudioDTOValue = 0x80000000;
72525aee3deSMauro Carvalho Chehab 			chan->AudioDTOUpdated = 1;
72625aee3deSMauro Carvalho Chehab 		}
72725aee3deSMauro Carvalho Chehab 		if (chan->mode & NGENE_IO_TSIN)
72825aee3deSMauro Carvalho Chehab 			chan->pBufferExchange = tsin_exchange;
72925aee3deSMauro Carvalho Chehab 		spin_unlock_irq(&chan->state_lock);
7302827a440SMauro Carvalho Chehab 	}
7316795bf62SDaniel Scheller 		/* else dev_info(pdev, "lock=%08x\n",
73225aee3deSMauro Carvalho Chehab 			   ngreadl(0x9310)); */
73325aee3deSMauro Carvalho Chehab 
73490979f04SBinoy Jayan 	mutex_lock(&dev->stream_mutex);
73525aee3deSMauro Carvalho Chehab 	ret = ngene_command_stream_control(dev, chan->number,
73625aee3deSMauro Carvalho Chehab 					   control, mode, flags);
73790979f04SBinoy Jayan 	mutex_unlock(&dev->stream_mutex);
73890979f04SBinoy Jayan 
73925aee3deSMauro Carvalho Chehab 	if (!ret)
74025aee3deSMauro Carvalho Chehab 		chan->running = state;
74125aee3deSMauro Carvalho Chehab 	else
7426795bf62SDaniel Scheller 		dev_err(pdev, "%s %d failed\n", __func__, state);
74325aee3deSMauro Carvalho Chehab 	if (!state) {
74425aee3deSMauro Carvalho Chehab 		spin_lock_irq(&chan->state_lock);
74525aee3deSMauro Carvalho Chehab 		chan->pBufferExchange = NULL;
74625aee3deSMauro Carvalho Chehab 		dvb_ringbuffer_flush(&dev->tsout_rbuf);
74725aee3deSMauro Carvalho Chehab 		spin_unlock_irq(&chan->state_lock);
74825aee3deSMauro Carvalho Chehab 	}
74925aee3deSMauro Carvalho Chehab }
75025aee3deSMauro Carvalho Chehab 
75125aee3deSMauro Carvalho Chehab 
75225aee3deSMauro Carvalho Chehab /****************************************************************************/
75325aee3deSMauro Carvalho Chehab /* nGene hardware init and release functions ********************************/
75425aee3deSMauro Carvalho Chehab /****************************************************************************/
75525aee3deSMauro Carvalho Chehab 
free_ringbuffer(struct ngene * dev,struct SRingBufferDescriptor * rb)75625aee3deSMauro Carvalho Chehab static void free_ringbuffer(struct ngene *dev, struct SRingBufferDescriptor *rb)
75725aee3deSMauro Carvalho Chehab {
75825aee3deSMauro Carvalho Chehab 	struct SBufferHeader *Cur = rb->Head;
75925aee3deSMauro Carvalho Chehab 	u32 j;
76025aee3deSMauro Carvalho Chehab 
76125aee3deSMauro Carvalho Chehab 	if (!Cur)
76225aee3deSMauro Carvalho Chehab 		return;
76325aee3deSMauro Carvalho Chehab 
76425aee3deSMauro Carvalho Chehab 	for (j = 0; j < rb->NumBuffers; j++, Cur = Cur->Next) {
76525aee3deSMauro Carvalho Chehab 		if (Cur->Buffer1)
766efb8225cSChristophe JAILLET 			dma_free_coherent(&dev->pci_dev->dev,
767efb8225cSChristophe JAILLET 					  rb->Buffer1Length, Cur->Buffer1,
76825aee3deSMauro Carvalho Chehab 					  Cur->scList1->Address);
76925aee3deSMauro Carvalho Chehab 
77025aee3deSMauro Carvalho Chehab 		if (Cur->Buffer2)
771efb8225cSChristophe JAILLET 			dma_free_coherent(&dev->pci_dev->dev,
772efb8225cSChristophe JAILLET 					  rb->Buffer2Length, Cur->Buffer2,
77325aee3deSMauro Carvalho Chehab 					  Cur->scList2->Address);
77425aee3deSMauro Carvalho Chehab 	}
77525aee3deSMauro Carvalho Chehab 
77625aee3deSMauro Carvalho Chehab 	if (rb->SCListMem)
777efb8225cSChristophe JAILLET 		dma_free_coherent(&dev->pci_dev->dev, rb->SCListMemSize,
77825aee3deSMauro Carvalho Chehab 				  rb->SCListMem, rb->PASCListMem);
77925aee3deSMauro Carvalho Chehab 
780efb8225cSChristophe JAILLET 	dma_free_coherent(&dev->pci_dev->dev, rb->MemSize, rb->Head,
781efb8225cSChristophe JAILLET 			  rb->PAHead);
78225aee3deSMauro Carvalho Chehab }
78325aee3deSMauro Carvalho Chehab 
free_idlebuffer(struct ngene * dev,struct SRingBufferDescriptor * rb,struct SRingBufferDescriptor * tb)78425aee3deSMauro Carvalho Chehab static void free_idlebuffer(struct ngene *dev,
78525aee3deSMauro Carvalho Chehab 		     struct SRingBufferDescriptor *rb,
78625aee3deSMauro Carvalho Chehab 		     struct SRingBufferDescriptor *tb)
78725aee3deSMauro Carvalho Chehab {
78825aee3deSMauro Carvalho Chehab 	int j;
78925aee3deSMauro Carvalho Chehab 	struct SBufferHeader *Cur = tb->Head;
79025aee3deSMauro Carvalho Chehab 
79125aee3deSMauro Carvalho Chehab 	if (!rb->Head)
79225aee3deSMauro Carvalho Chehab 		return;
79325aee3deSMauro Carvalho Chehab 	free_ringbuffer(dev, rb);
79425aee3deSMauro Carvalho Chehab 	for (j = 0; j < tb->NumBuffers; j++, Cur = Cur->Next) {
79525aee3deSMauro Carvalho Chehab 		Cur->Buffer2 = NULL;
79625aee3deSMauro Carvalho Chehab 		Cur->scList2 = NULL;
79725aee3deSMauro Carvalho Chehab 		Cur->ngeneBuffer.Address_of_first_entry_2 = 0;
79825aee3deSMauro Carvalho Chehab 		Cur->ngeneBuffer.Number_of_entries_2 = 0;
79925aee3deSMauro Carvalho Chehab 	}
80025aee3deSMauro Carvalho Chehab }
80125aee3deSMauro Carvalho Chehab 
free_common_buffers(struct ngene * dev)80225aee3deSMauro Carvalho Chehab static void free_common_buffers(struct ngene *dev)
80325aee3deSMauro Carvalho Chehab {
80425aee3deSMauro Carvalho Chehab 	u32 i;
80525aee3deSMauro Carvalho Chehab 	struct ngene_channel *chan;
80625aee3deSMauro Carvalho Chehab 
80725aee3deSMauro Carvalho Chehab 	for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) {
80825aee3deSMauro Carvalho Chehab 		chan = &dev->channel[i];
80925aee3deSMauro Carvalho Chehab 		free_idlebuffer(dev, &chan->TSIdleBuffer, &chan->TSRingBuffer);
81025aee3deSMauro Carvalho Chehab 		free_ringbuffer(dev, &chan->RingBuffer);
81125aee3deSMauro Carvalho Chehab 		free_ringbuffer(dev, &chan->TSRingBuffer);
81225aee3deSMauro Carvalho Chehab 	}
81325aee3deSMauro Carvalho Chehab 
81425aee3deSMauro Carvalho Chehab 	if (dev->OverflowBuffer)
815efb8225cSChristophe JAILLET 		dma_free_coherent(&dev->pci_dev->dev, OVERFLOW_BUFFER_SIZE,
81625aee3deSMauro Carvalho Chehab 				  dev->OverflowBuffer, dev->PAOverflowBuffer);
81725aee3deSMauro Carvalho Chehab 
81825aee3deSMauro Carvalho Chehab 	if (dev->FWInterfaceBuffer)
819efb8225cSChristophe JAILLET 		dma_free_coherent(&dev->pci_dev->dev, 4096,
82025aee3deSMauro Carvalho Chehab 				  dev->FWInterfaceBuffer,
82125aee3deSMauro Carvalho Chehab 				  dev->PAFWInterfaceBuffer);
82225aee3deSMauro Carvalho Chehab }
82325aee3deSMauro Carvalho Chehab 
82425aee3deSMauro Carvalho Chehab /****************************************************************************/
82525aee3deSMauro Carvalho Chehab /* Ring buffer handling *****************************************************/
82625aee3deSMauro Carvalho Chehab /****************************************************************************/
82725aee3deSMauro Carvalho Chehab 
create_ring_buffer(struct pci_dev * pci_dev,struct SRingBufferDescriptor * descr,u32 NumBuffers)82825aee3deSMauro Carvalho Chehab static int create_ring_buffer(struct pci_dev *pci_dev,
82925aee3deSMauro Carvalho Chehab 		       struct SRingBufferDescriptor *descr, u32 NumBuffers)
83025aee3deSMauro Carvalho Chehab {
83125aee3deSMauro Carvalho Chehab 	dma_addr_t tmp;
83225aee3deSMauro Carvalho Chehab 	struct SBufferHeader *Head;
83325aee3deSMauro Carvalho Chehab 	u32 i;
83425aee3deSMauro Carvalho Chehab 	u32 MemSize = SIZEOF_SBufferHeader * NumBuffers;
83525aee3deSMauro Carvalho Chehab 	u64 PARingBufferHead;
83625aee3deSMauro Carvalho Chehab 	u64 PARingBufferCur;
83725aee3deSMauro Carvalho Chehab 	u64 PARingBufferNext;
83825aee3deSMauro Carvalho Chehab 	struct SBufferHeader *Cur, *Next;
83925aee3deSMauro Carvalho Chehab 
84025aee3deSMauro Carvalho Chehab 	descr->Head = NULL;
84125aee3deSMauro Carvalho Chehab 	descr->MemSize = 0;
84225aee3deSMauro Carvalho Chehab 	descr->PAHead = 0;
84325aee3deSMauro Carvalho Chehab 	descr->NumBuffers = 0;
84425aee3deSMauro Carvalho Chehab 
84525aee3deSMauro Carvalho Chehab 	if (MemSize < 4096)
84625aee3deSMauro Carvalho Chehab 		MemSize = 4096;
84725aee3deSMauro Carvalho Chehab 
848efb8225cSChristophe JAILLET 	Head = dma_alloc_coherent(&pci_dev->dev, MemSize, &tmp, GFP_KERNEL);
84925aee3deSMauro Carvalho Chehab 	PARingBufferHead = tmp;
85025aee3deSMauro Carvalho Chehab 
85125aee3deSMauro Carvalho Chehab 	if (!Head)
85225aee3deSMauro Carvalho Chehab 		return -ENOMEM;
85325aee3deSMauro Carvalho Chehab 
85425aee3deSMauro Carvalho Chehab 	PARingBufferCur = PARingBufferHead;
85525aee3deSMauro Carvalho Chehab 	Cur = Head;
85625aee3deSMauro Carvalho Chehab 
85725aee3deSMauro Carvalho Chehab 	for (i = 0; i < NumBuffers - 1; i++) {
85825aee3deSMauro Carvalho Chehab 		Next = (struct SBufferHeader *)
85925aee3deSMauro Carvalho Chehab 			(((u8 *) Cur) + SIZEOF_SBufferHeader);
86025aee3deSMauro Carvalho Chehab 		PARingBufferNext = PARingBufferCur + SIZEOF_SBufferHeader;
86125aee3deSMauro Carvalho Chehab 		Cur->Next = Next;
86225aee3deSMauro Carvalho Chehab 		Cur->ngeneBuffer.Next = PARingBufferNext;
86325aee3deSMauro Carvalho Chehab 		Cur = Next;
86425aee3deSMauro Carvalho Chehab 		PARingBufferCur = PARingBufferNext;
86525aee3deSMauro Carvalho Chehab 	}
86625aee3deSMauro Carvalho Chehab 	/* Last Buffer points back to first one */
86725aee3deSMauro Carvalho Chehab 	Cur->Next = Head;
86825aee3deSMauro Carvalho Chehab 	Cur->ngeneBuffer.Next = PARingBufferHead;
86925aee3deSMauro Carvalho Chehab 
87025aee3deSMauro Carvalho Chehab 	descr->Head       = Head;
87125aee3deSMauro Carvalho Chehab 	descr->MemSize    = MemSize;
87225aee3deSMauro Carvalho Chehab 	descr->PAHead     = PARingBufferHead;
87325aee3deSMauro Carvalho Chehab 	descr->NumBuffers = NumBuffers;
87425aee3deSMauro Carvalho Chehab 
87525aee3deSMauro Carvalho Chehab 	return 0;
87625aee3deSMauro Carvalho Chehab }
87725aee3deSMauro Carvalho Chehab 
AllocateRingBuffers(struct pci_dev * pci_dev,dma_addr_t of,struct SRingBufferDescriptor * pRingBuffer,u32 Buffer1Length,u32 Buffer2Length)87825aee3deSMauro Carvalho Chehab static int AllocateRingBuffers(struct pci_dev *pci_dev,
87925aee3deSMauro Carvalho Chehab 			       dma_addr_t of,
88025aee3deSMauro Carvalho Chehab 			       struct SRingBufferDescriptor *pRingBuffer,
88125aee3deSMauro Carvalho Chehab 			       u32 Buffer1Length, u32 Buffer2Length)
88225aee3deSMauro Carvalho Chehab {
88325aee3deSMauro Carvalho Chehab 	dma_addr_t tmp;
88425aee3deSMauro Carvalho Chehab 	u32 i, j;
88525aee3deSMauro Carvalho Chehab 	u32 SCListMemSize = pRingBuffer->NumBuffers
88625aee3deSMauro Carvalho Chehab 		* ((Buffer2Length != 0) ? (NUM_SCATTER_GATHER_ENTRIES * 2) :
88725aee3deSMauro Carvalho Chehab 		    NUM_SCATTER_GATHER_ENTRIES)
88825aee3deSMauro Carvalho Chehab 		* sizeof(struct HW_SCATTER_GATHER_ELEMENT);
88925aee3deSMauro Carvalho Chehab 
89025aee3deSMauro Carvalho Chehab 	u64 PASCListMem;
89125aee3deSMauro Carvalho Chehab 	struct HW_SCATTER_GATHER_ELEMENT *SCListEntry;
89225aee3deSMauro Carvalho Chehab 	u64 PASCListEntry;
89325aee3deSMauro Carvalho Chehab 	struct SBufferHeader *Cur;
89425aee3deSMauro Carvalho Chehab 	void *SCListMem;
89525aee3deSMauro Carvalho Chehab 
89625aee3deSMauro Carvalho Chehab 	if (SCListMemSize < 4096)
89725aee3deSMauro Carvalho Chehab 		SCListMemSize = 4096;
89825aee3deSMauro Carvalho Chehab 
899efb8225cSChristophe JAILLET 	SCListMem = dma_alloc_coherent(&pci_dev->dev, SCListMemSize, &tmp,
900efb8225cSChristophe JAILLET 				       GFP_KERNEL);
90125aee3deSMauro Carvalho Chehab 
90225aee3deSMauro Carvalho Chehab 	PASCListMem = tmp;
90325aee3deSMauro Carvalho Chehab 	if (SCListMem == NULL)
90425aee3deSMauro Carvalho Chehab 		return -ENOMEM;
90525aee3deSMauro Carvalho Chehab 
90625aee3deSMauro Carvalho Chehab 	pRingBuffer->SCListMem = SCListMem;
90725aee3deSMauro Carvalho Chehab 	pRingBuffer->PASCListMem = PASCListMem;
90825aee3deSMauro Carvalho Chehab 	pRingBuffer->SCListMemSize = SCListMemSize;
90925aee3deSMauro Carvalho Chehab 	pRingBuffer->Buffer1Length = Buffer1Length;
91025aee3deSMauro Carvalho Chehab 	pRingBuffer->Buffer2Length = Buffer2Length;
91125aee3deSMauro Carvalho Chehab 
91225aee3deSMauro Carvalho Chehab 	SCListEntry = SCListMem;
91325aee3deSMauro Carvalho Chehab 	PASCListEntry = PASCListMem;
91425aee3deSMauro Carvalho Chehab 	Cur = pRingBuffer->Head;
91525aee3deSMauro Carvalho Chehab 
91625aee3deSMauro Carvalho Chehab 	for (i = 0; i < pRingBuffer->NumBuffers; i += 1, Cur = Cur->Next) {
91725aee3deSMauro Carvalho Chehab 		u64 PABuffer;
91825aee3deSMauro Carvalho Chehab 
919efb8225cSChristophe JAILLET 		void *Buffer = dma_alloc_coherent(&pci_dev->dev,
920efb8225cSChristophe JAILLET 						  Buffer1Length, &tmp, GFP_KERNEL);
92125aee3deSMauro Carvalho Chehab 		PABuffer = tmp;
92225aee3deSMauro Carvalho Chehab 
92325aee3deSMauro Carvalho Chehab 		if (Buffer == NULL)
92425aee3deSMauro Carvalho Chehab 			return -ENOMEM;
92525aee3deSMauro Carvalho Chehab 
92625aee3deSMauro Carvalho Chehab 		Cur->Buffer1 = Buffer;
92725aee3deSMauro Carvalho Chehab 
92825aee3deSMauro Carvalho Chehab 		SCListEntry->Address = PABuffer;
92925aee3deSMauro Carvalho Chehab 		SCListEntry->Length  = Buffer1Length;
93025aee3deSMauro Carvalho Chehab 
93125aee3deSMauro Carvalho Chehab 		Cur->scList1 = SCListEntry;
93225aee3deSMauro Carvalho Chehab 		Cur->ngeneBuffer.Address_of_first_entry_1 = PASCListEntry;
93325aee3deSMauro Carvalho Chehab 		Cur->ngeneBuffer.Number_of_entries_1 =
93425aee3deSMauro Carvalho Chehab 			NUM_SCATTER_GATHER_ENTRIES;
93525aee3deSMauro Carvalho Chehab 
93625aee3deSMauro Carvalho Chehab 		SCListEntry += 1;
93725aee3deSMauro Carvalho Chehab 		PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT);
93825aee3deSMauro Carvalho Chehab 
93925aee3deSMauro Carvalho Chehab #if NUM_SCATTER_GATHER_ENTRIES > 1
94025aee3deSMauro Carvalho Chehab 		for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j += 1) {
94125aee3deSMauro Carvalho Chehab 			SCListEntry->Address = of;
94225aee3deSMauro Carvalho Chehab 			SCListEntry->Length = OVERFLOW_BUFFER_SIZE;
94325aee3deSMauro Carvalho Chehab 			SCListEntry += 1;
94425aee3deSMauro Carvalho Chehab 			PASCListEntry +=
94525aee3deSMauro Carvalho Chehab 				sizeof(struct HW_SCATTER_GATHER_ELEMENT);
94625aee3deSMauro Carvalho Chehab 		}
94725aee3deSMauro Carvalho Chehab #endif
94825aee3deSMauro Carvalho Chehab 
94925aee3deSMauro Carvalho Chehab 		if (!Buffer2Length)
95025aee3deSMauro Carvalho Chehab 			continue;
95125aee3deSMauro Carvalho Chehab 
952efb8225cSChristophe JAILLET 		Buffer = dma_alloc_coherent(&pci_dev->dev, Buffer2Length,
953efb8225cSChristophe JAILLET 					    &tmp, GFP_KERNEL);
95425aee3deSMauro Carvalho Chehab 		PABuffer = tmp;
95525aee3deSMauro Carvalho Chehab 
95625aee3deSMauro Carvalho Chehab 		if (Buffer == NULL)
95725aee3deSMauro Carvalho Chehab 			return -ENOMEM;
95825aee3deSMauro Carvalho Chehab 
95925aee3deSMauro Carvalho Chehab 		Cur->Buffer2 = Buffer;
96025aee3deSMauro Carvalho Chehab 
96125aee3deSMauro Carvalho Chehab 		SCListEntry->Address = PABuffer;
96225aee3deSMauro Carvalho Chehab 		SCListEntry->Length  = Buffer2Length;
96325aee3deSMauro Carvalho Chehab 
96425aee3deSMauro Carvalho Chehab 		Cur->scList2 = SCListEntry;
96525aee3deSMauro Carvalho Chehab 		Cur->ngeneBuffer.Address_of_first_entry_2 = PASCListEntry;
96625aee3deSMauro Carvalho Chehab 		Cur->ngeneBuffer.Number_of_entries_2 =
96725aee3deSMauro Carvalho Chehab 			NUM_SCATTER_GATHER_ENTRIES;
96825aee3deSMauro Carvalho Chehab 
96925aee3deSMauro Carvalho Chehab 		SCListEntry   += 1;
97025aee3deSMauro Carvalho Chehab 		PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT);
97125aee3deSMauro Carvalho Chehab 
97225aee3deSMauro Carvalho Chehab #if NUM_SCATTER_GATHER_ENTRIES > 1
97325aee3deSMauro Carvalho Chehab 		for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j++) {
97425aee3deSMauro Carvalho Chehab 			SCListEntry->Address = of;
97525aee3deSMauro Carvalho Chehab 			SCListEntry->Length = OVERFLOW_BUFFER_SIZE;
97625aee3deSMauro Carvalho Chehab 			SCListEntry += 1;
97725aee3deSMauro Carvalho Chehab 			PASCListEntry +=
97825aee3deSMauro Carvalho Chehab 				sizeof(struct HW_SCATTER_GATHER_ELEMENT);
97925aee3deSMauro Carvalho Chehab 		}
98025aee3deSMauro Carvalho Chehab #endif
98125aee3deSMauro Carvalho Chehab 
98225aee3deSMauro Carvalho Chehab 	}
98325aee3deSMauro Carvalho Chehab 
9844df16f70SPeter Senna Tschudin 	return 0;
98525aee3deSMauro Carvalho Chehab }
98625aee3deSMauro Carvalho Chehab 
FillTSIdleBuffer(struct SRingBufferDescriptor * pIdleBuffer,struct SRingBufferDescriptor * pRingBuffer)98725aee3deSMauro Carvalho Chehab static int FillTSIdleBuffer(struct SRingBufferDescriptor *pIdleBuffer,
98825aee3deSMauro Carvalho Chehab 			    struct SRingBufferDescriptor *pRingBuffer)
98925aee3deSMauro Carvalho Chehab {
99025aee3deSMauro Carvalho Chehab 	/* Copy pointer to scatter gather list in TSRingbuffer
99125aee3deSMauro Carvalho Chehab 	   structure for buffer 2
99225aee3deSMauro Carvalho Chehab 	   Load number of buffer
99325aee3deSMauro Carvalho Chehab 	*/
99425aee3deSMauro Carvalho Chehab 	u32 n = pRingBuffer->NumBuffers;
99525aee3deSMauro Carvalho Chehab 
99625aee3deSMauro Carvalho Chehab 	/* Point to first buffer entry */
99725aee3deSMauro Carvalho Chehab 	struct SBufferHeader *Cur = pRingBuffer->Head;
99825aee3deSMauro Carvalho Chehab 	int i;
99916790554SMauro Carvalho Chehab 	/* Loop through all buffer and set Buffer 2 pointers to TSIdlebuffer */
100025aee3deSMauro Carvalho Chehab 	for (i = 0; i < n; i++) {
100125aee3deSMauro Carvalho Chehab 		Cur->Buffer2 = pIdleBuffer->Head->Buffer1;
100225aee3deSMauro Carvalho Chehab 		Cur->scList2 = pIdleBuffer->Head->scList1;
100325aee3deSMauro Carvalho Chehab 		Cur->ngeneBuffer.Address_of_first_entry_2 =
100425aee3deSMauro Carvalho Chehab 			pIdleBuffer->Head->ngeneBuffer.
100525aee3deSMauro Carvalho Chehab 			Address_of_first_entry_1;
100625aee3deSMauro Carvalho Chehab 		Cur->ngeneBuffer.Number_of_entries_2 =
100725aee3deSMauro Carvalho Chehab 			pIdleBuffer->Head->ngeneBuffer.Number_of_entries_1;
100825aee3deSMauro Carvalho Chehab 		Cur = Cur->Next;
100925aee3deSMauro Carvalho Chehab 	}
10104df16f70SPeter Senna Tschudin 	return 0;
101125aee3deSMauro Carvalho Chehab }
101225aee3deSMauro Carvalho Chehab 
101325aee3deSMauro Carvalho Chehab static u32 RingBufferSizes[MAX_STREAM] = {
101425aee3deSMauro Carvalho Chehab 	RING_SIZE_VIDEO,
101525aee3deSMauro Carvalho Chehab 	RING_SIZE_VIDEO,
101625aee3deSMauro Carvalho Chehab 	RING_SIZE_AUDIO,
101725aee3deSMauro Carvalho Chehab 	RING_SIZE_AUDIO,
101825aee3deSMauro Carvalho Chehab 	RING_SIZE_AUDIO,
101925aee3deSMauro Carvalho Chehab };
102025aee3deSMauro Carvalho Chehab 
102125aee3deSMauro Carvalho Chehab static u32 Buffer1Sizes[MAX_STREAM] = {
102225aee3deSMauro Carvalho Chehab 	MAX_VIDEO_BUFFER_SIZE,
102325aee3deSMauro Carvalho Chehab 	MAX_VIDEO_BUFFER_SIZE,
102425aee3deSMauro Carvalho Chehab 	MAX_AUDIO_BUFFER_SIZE,
102525aee3deSMauro Carvalho Chehab 	MAX_AUDIO_BUFFER_SIZE,
102625aee3deSMauro Carvalho Chehab 	MAX_AUDIO_BUFFER_SIZE
102725aee3deSMauro Carvalho Chehab };
102825aee3deSMauro Carvalho Chehab 
102925aee3deSMauro Carvalho Chehab static u32 Buffer2Sizes[MAX_STREAM] = {
103025aee3deSMauro Carvalho Chehab 	MAX_VBI_BUFFER_SIZE,
103125aee3deSMauro Carvalho Chehab 	MAX_VBI_BUFFER_SIZE,
103225aee3deSMauro Carvalho Chehab 	0,
103325aee3deSMauro Carvalho Chehab 	0,
103425aee3deSMauro Carvalho Chehab 	0
103525aee3deSMauro Carvalho Chehab };
103625aee3deSMauro Carvalho Chehab 
103725aee3deSMauro Carvalho Chehab 
AllocCommonBuffers(struct ngene * dev)103825aee3deSMauro Carvalho Chehab static int AllocCommonBuffers(struct ngene *dev)
103925aee3deSMauro Carvalho Chehab {
104025aee3deSMauro Carvalho Chehab 	int status = 0, i;
104125aee3deSMauro Carvalho Chehab 
1042efb8225cSChristophe JAILLET 	dev->FWInterfaceBuffer = dma_alloc_coherent(&dev->pci_dev->dev, 4096,
1043efb8225cSChristophe JAILLET 						    &dev->PAFWInterfaceBuffer,
1044efb8225cSChristophe JAILLET 						    GFP_KERNEL);
104525aee3deSMauro Carvalho Chehab 	if (!dev->FWInterfaceBuffer)
104625aee3deSMauro Carvalho Chehab 		return -ENOMEM;
104725aee3deSMauro Carvalho Chehab 	dev->hosttongene = dev->FWInterfaceBuffer;
104825aee3deSMauro Carvalho Chehab 	dev->ngenetohost = dev->FWInterfaceBuffer + 256;
104925aee3deSMauro Carvalho Chehab 	dev->EventBuffer = dev->FWInterfaceBuffer + 512;
105025aee3deSMauro Carvalho Chehab 
1051efb8225cSChristophe JAILLET 	dev->OverflowBuffer = dma_alloc_coherent(&dev->pci_dev->dev,
105225aee3deSMauro Carvalho Chehab 						 OVERFLOW_BUFFER_SIZE,
1053efb8225cSChristophe JAILLET 						 &dev->PAOverflowBuffer, GFP_KERNEL);
105425aee3deSMauro Carvalho Chehab 	if (!dev->OverflowBuffer)
105525aee3deSMauro Carvalho Chehab 		return -ENOMEM;
105625aee3deSMauro Carvalho Chehab 
105725aee3deSMauro Carvalho Chehab 	for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) {
105825aee3deSMauro Carvalho Chehab 		int type = dev->card_info->io_type[i];
105925aee3deSMauro Carvalho Chehab 
106025aee3deSMauro Carvalho Chehab 		dev->channel[i].State = KSSTATE_STOP;
106125aee3deSMauro Carvalho Chehab 
106225aee3deSMauro Carvalho Chehab 		if (type & (NGENE_IO_TV | NGENE_IO_HDTV | NGENE_IO_AIN)) {
106325aee3deSMauro Carvalho Chehab 			status = create_ring_buffer(dev->pci_dev,
106425aee3deSMauro Carvalho Chehab 						    &dev->channel[i].RingBuffer,
106525aee3deSMauro Carvalho Chehab 						    RingBufferSizes[i]);
106625aee3deSMauro Carvalho Chehab 			if (status < 0)
106725aee3deSMauro Carvalho Chehab 				break;
106825aee3deSMauro Carvalho Chehab 
106925aee3deSMauro Carvalho Chehab 			if (type & (NGENE_IO_TV | NGENE_IO_AIN)) {
107025aee3deSMauro Carvalho Chehab 				status = AllocateRingBuffers(dev->pci_dev,
107125aee3deSMauro Carvalho Chehab 							     dev->
107225aee3deSMauro Carvalho Chehab 							     PAOverflowBuffer,
107325aee3deSMauro Carvalho Chehab 							     &dev->channel[i].
107425aee3deSMauro Carvalho Chehab 							     RingBuffer,
107525aee3deSMauro Carvalho Chehab 							     Buffer1Sizes[i],
107625aee3deSMauro Carvalho Chehab 							     Buffer2Sizes[i]);
107725aee3deSMauro Carvalho Chehab 				if (status < 0)
107825aee3deSMauro Carvalho Chehab 					break;
107925aee3deSMauro Carvalho Chehab 			} else if (type & NGENE_IO_HDTV) {
108025aee3deSMauro Carvalho Chehab 				status = AllocateRingBuffers(dev->pci_dev,
108125aee3deSMauro Carvalho Chehab 							     dev->
108225aee3deSMauro Carvalho Chehab 							     PAOverflowBuffer,
108325aee3deSMauro Carvalho Chehab 							     &dev->channel[i].
108425aee3deSMauro Carvalho Chehab 							     RingBuffer,
108525aee3deSMauro Carvalho Chehab 							   MAX_HDTV_BUFFER_SIZE,
108625aee3deSMauro Carvalho Chehab 							     0);
108725aee3deSMauro Carvalho Chehab 				if (status < 0)
108825aee3deSMauro Carvalho Chehab 					break;
108925aee3deSMauro Carvalho Chehab 			}
109025aee3deSMauro Carvalho Chehab 		}
109125aee3deSMauro Carvalho Chehab 
109225aee3deSMauro Carvalho Chehab 		if (type & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
109325aee3deSMauro Carvalho Chehab 
109425aee3deSMauro Carvalho Chehab 			status = create_ring_buffer(dev->pci_dev,
109525aee3deSMauro Carvalho Chehab 						    &dev->channel[i].
109625aee3deSMauro Carvalho Chehab 						    TSRingBuffer, RING_SIZE_TS);
109725aee3deSMauro Carvalho Chehab 			if (status < 0)
109825aee3deSMauro Carvalho Chehab 				break;
109925aee3deSMauro Carvalho Chehab 
110025aee3deSMauro Carvalho Chehab 			status = AllocateRingBuffers(dev->pci_dev,
110125aee3deSMauro Carvalho Chehab 						     dev->PAOverflowBuffer,
110225aee3deSMauro Carvalho Chehab 						     &dev->channel[i].
110325aee3deSMauro Carvalho Chehab 						     TSRingBuffer,
110425aee3deSMauro Carvalho Chehab 						     MAX_TS_BUFFER_SIZE, 0);
110525aee3deSMauro Carvalho Chehab 			if (status)
110625aee3deSMauro Carvalho Chehab 				break;
110725aee3deSMauro Carvalho Chehab 		}
110825aee3deSMauro Carvalho Chehab 
110925aee3deSMauro Carvalho Chehab 		if (type & NGENE_IO_TSOUT) {
111025aee3deSMauro Carvalho Chehab 			status = create_ring_buffer(dev->pci_dev,
111125aee3deSMauro Carvalho Chehab 						    &dev->channel[i].
111225aee3deSMauro Carvalho Chehab 						    TSIdleBuffer, 1);
111325aee3deSMauro Carvalho Chehab 			if (status < 0)
111425aee3deSMauro Carvalho Chehab 				break;
111525aee3deSMauro Carvalho Chehab 			status = AllocateRingBuffers(dev->pci_dev,
111625aee3deSMauro Carvalho Chehab 						     dev->PAOverflowBuffer,
111725aee3deSMauro Carvalho Chehab 						     &dev->channel[i].
111825aee3deSMauro Carvalho Chehab 						     TSIdleBuffer,
111925aee3deSMauro Carvalho Chehab 						     MAX_TS_BUFFER_SIZE, 0);
112025aee3deSMauro Carvalho Chehab 			if (status)
112125aee3deSMauro Carvalho Chehab 				break;
112225aee3deSMauro Carvalho Chehab 			FillTSIdleBuffer(&dev->channel[i].TSIdleBuffer,
112325aee3deSMauro Carvalho Chehab 					 &dev->channel[i].TSRingBuffer);
112425aee3deSMauro Carvalho Chehab 		}
112525aee3deSMauro Carvalho Chehab 	}
112625aee3deSMauro Carvalho Chehab 	return status;
112725aee3deSMauro Carvalho Chehab }
112825aee3deSMauro Carvalho Chehab 
ngene_release_buffers(struct ngene * dev)112925aee3deSMauro Carvalho Chehab static void ngene_release_buffers(struct ngene *dev)
113025aee3deSMauro Carvalho Chehab {
113125aee3deSMauro Carvalho Chehab 	if (dev->iomem)
113225aee3deSMauro Carvalho Chehab 		iounmap(dev->iomem);
113325aee3deSMauro Carvalho Chehab 	free_common_buffers(dev);
113425aee3deSMauro Carvalho Chehab 	vfree(dev->tsout_buf);
113525aee3deSMauro Carvalho Chehab 	vfree(dev->tsin_buf);
113625aee3deSMauro Carvalho Chehab 	vfree(dev->ain_buf);
113725aee3deSMauro Carvalho Chehab 	vfree(dev->vin_buf);
113825aee3deSMauro Carvalho Chehab 	vfree(dev);
113925aee3deSMauro Carvalho Chehab }
114025aee3deSMauro Carvalho Chehab 
ngene_get_buffers(struct ngene * dev)114125aee3deSMauro Carvalho Chehab static int ngene_get_buffers(struct ngene *dev)
114225aee3deSMauro Carvalho Chehab {
114325aee3deSMauro Carvalho Chehab 	if (AllocCommonBuffers(dev))
114425aee3deSMauro Carvalho Chehab 		return -ENOMEM;
114525aee3deSMauro Carvalho Chehab 	if (dev->card_info->io_type[4] & NGENE_IO_TSOUT) {
114625aee3deSMauro Carvalho Chehab 		dev->tsout_buf = vmalloc(TSOUT_BUF_SIZE);
114725aee3deSMauro Carvalho Chehab 		if (!dev->tsout_buf)
114825aee3deSMauro Carvalho Chehab 			return -ENOMEM;
114925aee3deSMauro Carvalho Chehab 		dvb_ringbuffer_init(&dev->tsout_rbuf,
115025aee3deSMauro Carvalho Chehab 				    dev->tsout_buf, TSOUT_BUF_SIZE);
115125aee3deSMauro Carvalho Chehab 	}
115225aee3deSMauro Carvalho Chehab 	if (dev->card_info->io_type[2]&NGENE_IO_TSIN) {
115325aee3deSMauro Carvalho Chehab 		dev->tsin_buf = vmalloc(TSIN_BUF_SIZE);
115425aee3deSMauro Carvalho Chehab 		if (!dev->tsin_buf)
115525aee3deSMauro Carvalho Chehab 			return -ENOMEM;
115625aee3deSMauro Carvalho Chehab 		dvb_ringbuffer_init(&dev->tsin_rbuf,
115725aee3deSMauro Carvalho Chehab 				    dev->tsin_buf, TSIN_BUF_SIZE);
115825aee3deSMauro Carvalho Chehab 	}
115925aee3deSMauro Carvalho Chehab 	if (dev->card_info->io_type[2] & NGENE_IO_AIN) {
116025aee3deSMauro Carvalho Chehab 		dev->ain_buf = vmalloc(AIN_BUF_SIZE);
116125aee3deSMauro Carvalho Chehab 		if (!dev->ain_buf)
116225aee3deSMauro Carvalho Chehab 			return -ENOMEM;
116325aee3deSMauro Carvalho Chehab 		dvb_ringbuffer_init(&dev->ain_rbuf, dev->ain_buf, AIN_BUF_SIZE);
116425aee3deSMauro Carvalho Chehab 	}
116525aee3deSMauro Carvalho Chehab 	if (dev->card_info->io_type[0] & NGENE_IO_HDTV) {
116625aee3deSMauro Carvalho Chehab 		dev->vin_buf = vmalloc(VIN_BUF_SIZE);
116725aee3deSMauro Carvalho Chehab 		if (!dev->vin_buf)
116825aee3deSMauro Carvalho Chehab 			return -ENOMEM;
116925aee3deSMauro Carvalho Chehab 		dvb_ringbuffer_init(&dev->vin_rbuf, dev->vin_buf, VIN_BUF_SIZE);
117025aee3deSMauro Carvalho Chehab 	}
117125aee3deSMauro Carvalho Chehab 	dev->iomem = ioremap(pci_resource_start(dev->pci_dev, 0),
117225aee3deSMauro Carvalho Chehab 			     pci_resource_len(dev->pci_dev, 0));
117325aee3deSMauro Carvalho Chehab 	if (!dev->iomem)
117425aee3deSMauro Carvalho Chehab 		return -ENOMEM;
117525aee3deSMauro Carvalho Chehab 
117625aee3deSMauro Carvalho Chehab 	return 0;
117725aee3deSMauro Carvalho Chehab }
117825aee3deSMauro Carvalho Chehab 
ngene_init(struct ngene * dev)117925aee3deSMauro Carvalho Chehab static void ngene_init(struct ngene *dev)
118025aee3deSMauro Carvalho Chehab {
11816795bf62SDaniel Scheller 	struct device *pdev = &dev->pci_dev->dev;
118225aee3deSMauro Carvalho Chehab 	int i;
118325aee3deSMauro Carvalho Chehab 
11846027ff6bSAllen Pais 	tasklet_setup(&dev->event_tasklet, event_tasklet);
118525aee3deSMauro Carvalho Chehab 
118625aee3deSMauro Carvalho Chehab 	memset_io(dev->iomem + 0xc000, 0x00, 0x220);
118725aee3deSMauro Carvalho Chehab 	memset_io(dev->iomem + 0xc400, 0x00, 0x100);
118825aee3deSMauro Carvalho Chehab 
118925aee3deSMauro Carvalho Chehab 	for (i = 0; i < MAX_STREAM; i++) {
119025aee3deSMauro Carvalho Chehab 		dev->channel[i].dev = dev;
119125aee3deSMauro Carvalho Chehab 		dev->channel[i].number = i;
119225aee3deSMauro Carvalho Chehab 	}
119325aee3deSMauro Carvalho Chehab 
119425aee3deSMauro Carvalho Chehab 	dev->fw_interface_version = 0;
119525aee3deSMauro Carvalho Chehab 
119625aee3deSMauro Carvalho Chehab 	ngwritel(0, NGENE_INT_ENABLE);
119725aee3deSMauro Carvalho Chehab 
119825aee3deSMauro Carvalho Chehab 	dev->icounts = ngreadl(NGENE_INT_COUNTS);
119925aee3deSMauro Carvalho Chehab 
120025aee3deSMauro Carvalho Chehab 	dev->device_version = ngreadl(DEV_VER) & 0x0f;
12016795bf62SDaniel Scheller 	dev_info(pdev, "Device version %d\n", dev->device_version);
120225aee3deSMauro Carvalho Chehab }
120325aee3deSMauro Carvalho Chehab 
ngene_load_firm(struct ngene * dev)120425aee3deSMauro Carvalho Chehab static int ngene_load_firm(struct ngene *dev)
120525aee3deSMauro Carvalho Chehab {
12066795bf62SDaniel Scheller 	struct device *pdev = &dev->pci_dev->dev;
120725aee3deSMauro Carvalho Chehab 	u32 size;
120825aee3deSMauro Carvalho Chehab 	const struct firmware *fw = NULL;
120925aee3deSMauro Carvalho Chehab 	u8 *ngene_fw;
121025aee3deSMauro Carvalho Chehab 	char *fw_name;
121125aee3deSMauro Carvalho Chehab 	int err, version;
121225aee3deSMauro Carvalho Chehab 
121325aee3deSMauro Carvalho Chehab 	version = dev->card_info->fw_version;
121425aee3deSMauro Carvalho Chehab 
121525aee3deSMauro Carvalho Chehab 	switch (version) {
121625aee3deSMauro Carvalho Chehab 	default:
121725aee3deSMauro Carvalho Chehab 	case 15:
121825aee3deSMauro Carvalho Chehab 		version = 15;
121925aee3deSMauro Carvalho Chehab 		size = 23466;
122025aee3deSMauro Carvalho Chehab 		fw_name = "ngene_15.fw";
122125aee3deSMauro Carvalho Chehab 		dev->cmd_timeout_workaround = true;
122225aee3deSMauro Carvalho Chehab 		break;
122325aee3deSMauro Carvalho Chehab 	case 16:
122425aee3deSMauro Carvalho Chehab 		size = 23498;
122525aee3deSMauro Carvalho Chehab 		fw_name = "ngene_16.fw";
122625aee3deSMauro Carvalho Chehab 		dev->cmd_timeout_workaround = true;
122725aee3deSMauro Carvalho Chehab 		break;
122825aee3deSMauro Carvalho Chehab 	case 17:
122925aee3deSMauro Carvalho Chehab 		size = 24446;
123025aee3deSMauro Carvalho Chehab 		fw_name = "ngene_17.fw";
123125aee3deSMauro Carvalho Chehab 		dev->cmd_timeout_workaround = true;
123225aee3deSMauro Carvalho Chehab 		break;
123325aee3deSMauro Carvalho Chehab 	case 18:
123425aee3deSMauro Carvalho Chehab 		size = 0;
123525aee3deSMauro Carvalho Chehab 		fw_name = "ngene_18.fw";
123625aee3deSMauro Carvalho Chehab 		break;
123725aee3deSMauro Carvalho Chehab 	}
123825aee3deSMauro Carvalho Chehab 
123925aee3deSMauro Carvalho Chehab 	if (request_firmware(&fw, fw_name, &dev->pci_dev->dev) < 0) {
12406795bf62SDaniel Scheller 		dev_err(pdev, "Could not load firmware file %s.\n", fw_name);
12416795bf62SDaniel Scheller 		dev_info(pdev, "Copy %s to your hotplug directory!\n",
12426795bf62SDaniel Scheller 			 fw_name);
124325aee3deSMauro Carvalho Chehab 		return -1;
124425aee3deSMauro Carvalho Chehab 	}
124525aee3deSMauro Carvalho Chehab 	if (size == 0)
124625aee3deSMauro Carvalho Chehab 		size = fw->size;
124725aee3deSMauro Carvalho Chehab 	if (size != fw->size) {
12486795bf62SDaniel Scheller 		dev_err(pdev, "Firmware %s has invalid size!", fw_name);
124925aee3deSMauro Carvalho Chehab 		err = -1;
125025aee3deSMauro Carvalho Chehab 	} else {
12516795bf62SDaniel Scheller 		dev_info(pdev, "Loading firmware file %s.\n", fw_name);
125225aee3deSMauro Carvalho Chehab 		ngene_fw = (u8 *) fw->data;
125325aee3deSMauro Carvalho Chehab 		err = ngene_command_load_firmware(dev, ngene_fw, size);
125425aee3deSMauro Carvalho Chehab 	}
125525aee3deSMauro Carvalho Chehab 
125625aee3deSMauro Carvalho Chehab 	release_firmware(fw);
125725aee3deSMauro Carvalho Chehab 
125825aee3deSMauro Carvalho Chehab 	return err;
125925aee3deSMauro Carvalho Chehab }
126025aee3deSMauro Carvalho Chehab 
ngene_stop(struct ngene * dev)126125aee3deSMauro Carvalho Chehab static void ngene_stop(struct ngene *dev)
126225aee3deSMauro Carvalho Chehab {
12631439cdb0SBinoy Jayan 	mutex_destroy(&dev->cmd_mutex);
126425aee3deSMauro Carvalho Chehab 	i2c_del_adapter(&(dev->channel[0].i2c_adapter));
126525aee3deSMauro Carvalho Chehab 	i2c_del_adapter(&(dev->channel[1].i2c_adapter));
126625aee3deSMauro Carvalho Chehab 	ngwritel(0, NGENE_INT_ENABLE);
126725aee3deSMauro Carvalho Chehab 	ngwritel(0, NGENE_COMMAND);
126825aee3deSMauro Carvalho Chehab 	ngwritel(0, NGENE_COMMAND_HI);
126925aee3deSMauro Carvalho Chehab 	ngwritel(0, NGENE_STATUS);
127025aee3deSMauro Carvalho Chehab 	ngwritel(0, NGENE_STATUS_HI);
127125aee3deSMauro Carvalho Chehab 	ngwritel(0, NGENE_EVENT);
127225aee3deSMauro Carvalho Chehab 	ngwritel(0, NGENE_EVENT_HI);
127325aee3deSMauro Carvalho Chehab 	free_irq(dev->pci_dev->irq, dev);
127425aee3deSMauro Carvalho Chehab #ifdef CONFIG_PCI_MSI
127525aee3deSMauro Carvalho Chehab 	if (dev->msi_enabled)
127625aee3deSMauro Carvalho Chehab 		pci_disable_msi(dev->pci_dev);
127725aee3deSMauro Carvalho Chehab #endif
127825aee3deSMauro Carvalho Chehab }
127925aee3deSMauro Carvalho Chehab 
ngene_buffer_config(struct ngene * dev)128025aee3deSMauro Carvalho Chehab static int ngene_buffer_config(struct ngene *dev)
128125aee3deSMauro Carvalho Chehab {
128225aee3deSMauro Carvalho Chehab 	int stat;
128325aee3deSMauro Carvalho Chehab 
128425aee3deSMauro Carvalho Chehab 	if (dev->card_info->fw_version >= 17) {
128525aee3deSMauro Carvalho Chehab 		u8 tsin12_config[6]   = { 0x60, 0x60, 0x00, 0x00, 0x00, 0x00 };
128625aee3deSMauro Carvalho Chehab 		u8 tsin1234_config[6] = { 0x30, 0x30, 0x00, 0x30, 0x30, 0x00 };
128725aee3deSMauro Carvalho Chehab 		u8 tsio1235_config[6] = { 0x30, 0x30, 0x00, 0x28, 0x00, 0x38 };
128825aee3deSMauro Carvalho Chehab 		u8 *bconf = tsin12_config;
128925aee3deSMauro Carvalho Chehab 
129025aee3deSMauro Carvalho Chehab 		if (dev->card_info->io_type[2]&NGENE_IO_TSIN &&
129125aee3deSMauro Carvalho Chehab 		    dev->card_info->io_type[3]&NGENE_IO_TSIN) {
129225aee3deSMauro Carvalho Chehab 			bconf = tsin1234_config;
129325aee3deSMauro Carvalho Chehab 			if (dev->card_info->io_type[4]&NGENE_IO_TSOUT &&
129425aee3deSMauro Carvalho Chehab 			    dev->ci.en)
129525aee3deSMauro Carvalho Chehab 				bconf = tsio1235_config;
129625aee3deSMauro Carvalho Chehab 		}
129725aee3deSMauro Carvalho Chehab 		stat = ngene_command_config_free_buf(dev, bconf);
129825aee3deSMauro Carvalho Chehab 	} else {
129925aee3deSMauro Carvalho Chehab 		int bconf = BUFFER_CONFIG_4422;
130025aee3deSMauro Carvalho Chehab 
130125aee3deSMauro Carvalho Chehab 		if (dev->card_info->io_type[3] == NGENE_IO_TSIN)
130225aee3deSMauro Carvalho Chehab 			bconf = BUFFER_CONFIG_3333;
130325aee3deSMauro Carvalho Chehab 		stat = ngene_command_config_buf(dev, bconf);
130425aee3deSMauro Carvalho Chehab 	}
130525aee3deSMauro Carvalho Chehab 	return stat;
130625aee3deSMauro Carvalho Chehab }
130725aee3deSMauro Carvalho Chehab 
130825aee3deSMauro Carvalho Chehab 
ngene_start(struct ngene * dev)130925aee3deSMauro Carvalho Chehab static int ngene_start(struct ngene *dev)
131025aee3deSMauro Carvalho Chehab {
131125aee3deSMauro Carvalho Chehab 	int stat;
131225aee3deSMauro Carvalho Chehab 	int i;
131325aee3deSMauro Carvalho Chehab 
131425aee3deSMauro Carvalho Chehab 	pci_set_master(dev->pci_dev);
131525aee3deSMauro Carvalho Chehab 	ngene_init(dev);
131625aee3deSMauro Carvalho Chehab 
131725aee3deSMauro Carvalho Chehab 	stat = request_irq(dev->pci_dev->irq, irq_handler,
131825aee3deSMauro Carvalho Chehab 			   IRQF_SHARED, "nGene",
131925aee3deSMauro Carvalho Chehab 			   (void *)dev);
132025aee3deSMauro Carvalho Chehab 	if (stat < 0)
132125aee3deSMauro Carvalho Chehab 		return stat;
132225aee3deSMauro Carvalho Chehab 
132325aee3deSMauro Carvalho Chehab 	init_waitqueue_head(&dev->cmd_wq);
132425aee3deSMauro Carvalho Chehab 	init_waitqueue_head(&dev->tx_wq);
132525aee3deSMauro Carvalho Chehab 	init_waitqueue_head(&dev->rx_wq);
13261439cdb0SBinoy Jayan 	mutex_init(&dev->cmd_mutex);
132790979f04SBinoy Jayan 	mutex_init(&dev->stream_mutex);
132825aee3deSMauro Carvalho Chehab 	sema_init(&dev->pll_mutex, 1);
1329bd7a85d3SBinoy Jayan 	mutex_init(&dev->i2c_switch_mutex);
133025aee3deSMauro Carvalho Chehab 	spin_lock_init(&dev->cmd_lock);
133125aee3deSMauro Carvalho Chehab 	for (i = 0; i < MAX_STREAM; i++)
133225aee3deSMauro Carvalho Chehab 		spin_lock_init(&dev->channel[i].state_lock);
133325aee3deSMauro Carvalho Chehab 	ngwritel(1, TIMESTAMPS);
133425aee3deSMauro Carvalho Chehab 
133525aee3deSMauro Carvalho Chehab 	ngwritel(1, NGENE_INT_ENABLE);
133625aee3deSMauro Carvalho Chehab 
133725aee3deSMauro Carvalho Chehab 	stat = ngene_load_firm(dev);
133825aee3deSMauro Carvalho Chehab 	if (stat < 0)
133925aee3deSMauro Carvalho Chehab 		goto fail;
134025aee3deSMauro Carvalho Chehab 
134125aee3deSMauro Carvalho Chehab #ifdef CONFIG_PCI_MSI
134225aee3deSMauro Carvalho Chehab 	/* enable MSI if kernel and card support it */
134325aee3deSMauro Carvalho Chehab 	if (pci_msi_enabled() && dev->card_info->msi_supported) {
1344492b183bSArnd Bergmann 		struct device *pdev = &dev->pci_dev->dev;
134525aee3deSMauro Carvalho Chehab 		unsigned long flags;
134625aee3deSMauro Carvalho Chehab 
134725aee3deSMauro Carvalho Chehab 		ngwritel(0, NGENE_INT_ENABLE);
134825aee3deSMauro Carvalho Chehab 		free_irq(dev->pci_dev->irq, dev);
134925aee3deSMauro Carvalho Chehab 		stat = pci_enable_msi(dev->pci_dev);
135025aee3deSMauro Carvalho Chehab 		if (stat) {
13516795bf62SDaniel Scheller 			dev_info(pdev, "MSI not available\n");
135225aee3deSMauro Carvalho Chehab 			flags = IRQF_SHARED;
135325aee3deSMauro Carvalho Chehab 		} else {
135425aee3deSMauro Carvalho Chehab 			flags = 0;
135525aee3deSMauro Carvalho Chehab 			dev->msi_enabled = true;
135625aee3deSMauro Carvalho Chehab 		}
135725aee3deSMauro Carvalho Chehab 		stat = request_irq(dev->pci_dev->irq, irq_handler,
135825aee3deSMauro Carvalho Chehab 					flags, "nGene", dev);
135925aee3deSMauro Carvalho Chehab 		if (stat < 0)
136025aee3deSMauro Carvalho Chehab 			goto fail2;
136125aee3deSMauro Carvalho Chehab 		ngwritel(1, NGENE_INT_ENABLE);
136225aee3deSMauro Carvalho Chehab 	}
136325aee3deSMauro Carvalho Chehab #endif
136425aee3deSMauro Carvalho Chehab 
136525aee3deSMauro Carvalho Chehab 	stat = ngene_i2c_init(dev, 0);
136625aee3deSMauro Carvalho Chehab 	if (stat < 0)
136725aee3deSMauro Carvalho Chehab 		goto fail;
136825aee3deSMauro Carvalho Chehab 
136925aee3deSMauro Carvalho Chehab 	stat = ngene_i2c_init(dev, 1);
137025aee3deSMauro Carvalho Chehab 	if (stat < 0)
137125aee3deSMauro Carvalho Chehab 		goto fail;
137225aee3deSMauro Carvalho Chehab 
137325aee3deSMauro Carvalho Chehab 	return 0;
137425aee3deSMauro Carvalho Chehab 
137525aee3deSMauro Carvalho Chehab fail:
137625aee3deSMauro Carvalho Chehab 	ngwritel(0, NGENE_INT_ENABLE);
137725aee3deSMauro Carvalho Chehab 	free_irq(dev->pci_dev->irq, dev);
137825aee3deSMauro Carvalho Chehab #ifdef CONFIG_PCI_MSI
137925aee3deSMauro Carvalho Chehab fail2:
138025aee3deSMauro Carvalho Chehab 	if (dev->msi_enabled)
138125aee3deSMauro Carvalho Chehab 		pci_disable_msi(dev->pci_dev);
138225aee3deSMauro Carvalho Chehab #endif
138325aee3deSMauro Carvalho Chehab 	return stat;
138425aee3deSMauro Carvalho Chehab }
138525aee3deSMauro Carvalho Chehab 
138625aee3deSMauro Carvalho Chehab /****************************************************************************/
138725aee3deSMauro Carvalho Chehab /****************************************************************************/
138825aee3deSMauro Carvalho Chehab /****************************************************************************/
138925aee3deSMauro Carvalho Chehab 
release_channel(struct ngene_channel * chan)139025aee3deSMauro Carvalho Chehab static void release_channel(struct ngene_channel *chan)
139125aee3deSMauro Carvalho Chehab {
139225aee3deSMauro Carvalho Chehab 	struct dvb_demux *dvbdemux = &chan->demux;
139325aee3deSMauro Carvalho Chehab 	struct ngene *dev = chan->dev;
139425aee3deSMauro Carvalho Chehab 
139525aee3deSMauro Carvalho Chehab 	if (chan->running)
139625aee3deSMauro Carvalho Chehab 		set_transfer(chan, 0);
139725aee3deSMauro Carvalho Chehab 
139825aee3deSMauro Carvalho Chehab 	tasklet_kill(&chan->demux_tasklet);
139925aee3deSMauro Carvalho Chehab 
140025aee3deSMauro Carvalho Chehab 	if (chan->ci_dev) {
140125aee3deSMauro Carvalho Chehab 		dvb_unregister_device(chan->ci_dev);
140225aee3deSMauro Carvalho Chehab 		chan->ci_dev = NULL;
140325aee3deSMauro Carvalho Chehab 	}
140425aee3deSMauro Carvalho Chehab 
140525aee3deSMauro Carvalho Chehab 	if (chan->fe2)
140625aee3deSMauro Carvalho Chehab 		dvb_unregister_frontend(chan->fe2);
140725aee3deSMauro Carvalho Chehab 
140825aee3deSMauro Carvalho Chehab 	if (chan->fe) {
140925aee3deSMauro Carvalho Chehab 		dvb_unregister_frontend(chan->fe);
14101c2ad82eSDaniel Scheller 
14111c2ad82eSDaniel Scheller 		/* release I2C client (tuner) if needed */
141278c4e082SDaniel Scheller 		if (chan->i2c_client_fe) {
141378c4e082SDaniel Scheller 			dvb_module_release(chan->i2c_client[0]);
14141c2ad82eSDaniel Scheller 			chan->i2c_client[0] = NULL;
14151c2ad82eSDaniel Scheller 		}
14161c2ad82eSDaniel Scheller 
141725aee3deSMauro Carvalho Chehab 		dvb_frontend_detach(chan->fe);
141825aee3deSMauro Carvalho Chehab 		chan->fe = NULL;
141925aee3deSMauro Carvalho Chehab 	}
142025aee3deSMauro Carvalho Chehab 
142125aee3deSMauro Carvalho Chehab 	if (chan->has_demux) {
142225aee3deSMauro Carvalho Chehab 		dvb_net_release(&chan->dvbnet);
142325aee3deSMauro Carvalho Chehab 		dvbdemux->dmx.close(&dvbdemux->dmx);
142425aee3deSMauro Carvalho Chehab 		dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
142525aee3deSMauro Carvalho Chehab 					      &chan->hw_frontend);
142625aee3deSMauro Carvalho Chehab 		dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
142725aee3deSMauro Carvalho Chehab 					      &chan->mem_frontend);
142825aee3deSMauro Carvalho Chehab 		dvb_dmxdev_release(&chan->dmxdev);
142925aee3deSMauro Carvalho Chehab 		dvb_dmx_release(&chan->demux);
143025aee3deSMauro Carvalho Chehab 		chan->has_demux = false;
143125aee3deSMauro Carvalho Chehab 	}
143225aee3deSMauro Carvalho Chehab 
143325aee3deSMauro Carvalho Chehab 	if (chan->has_adapter) {
143425aee3deSMauro Carvalho Chehab 		dvb_unregister_adapter(&dev->adapter[chan->number]);
143525aee3deSMauro Carvalho Chehab 		chan->has_adapter = false;
143625aee3deSMauro Carvalho Chehab 	}
143725aee3deSMauro Carvalho Chehab }
143825aee3deSMauro Carvalho Chehab 
init_channel(struct ngene_channel * chan)143925aee3deSMauro Carvalho Chehab static int init_channel(struct ngene_channel *chan)
144025aee3deSMauro Carvalho Chehab {
144125aee3deSMauro Carvalho Chehab 	int ret = 0, nr = chan->number;
144225aee3deSMauro Carvalho Chehab 	struct dvb_adapter *adapter = NULL;
144325aee3deSMauro Carvalho Chehab 	struct dvb_demux *dvbdemux = &chan->demux;
144425aee3deSMauro Carvalho Chehab 	struct ngene *dev = chan->dev;
144525aee3deSMauro Carvalho Chehab 	struct ngene_info *ni = dev->card_info;
144625aee3deSMauro Carvalho Chehab 	int io = ni->io_type[nr];
144725aee3deSMauro Carvalho Chehab 
14486027ff6bSAllen Pais 	tasklet_setup(&chan->demux_tasklet, demux_tasklet);
144925aee3deSMauro Carvalho Chehab 	chan->users = 0;
145025aee3deSMauro Carvalho Chehab 	chan->type = io;
145125aee3deSMauro Carvalho Chehab 	chan->mode = chan->type;	/* for now only one mode */
14521c2ad82eSDaniel Scheller 	chan->i2c_client_fe = 0;	/* be sure this is set to zero */
145325aee3deSMauro Carvalho Chehab 
145425aee3deSMauro Carvalho Chehab 	if (io & NGENE_IO_TSIN) {
145525aee3deSMauro Carvalho Chehab 		chan->fe = NULL;
145625aee3deSMauro Carvalho Chehab 		if (ni->demod_attach[nr]) {
145725aee3deSMauro Carvalho Chehab 			ret = ni->demod_attach[nr](chan);
145825aee3deSMauro Carvalho Chehab 			if (ret < 0)
145925aee3deSMauro Carvalho Chehab 				goto err;
146025aee3deSMauro Carvalho Chehab 		}
146125aee3deSMauro Carvalho Chehab 		if (chan->fe && ni->tuner_attach[nr]) {
146225aee3deSMauro Carvalho Chehab 			ret = ni->tuner_attach[nr](chan);
146325aee3deSMauro Carvalho Chehab 			if (ret < 0)
146425aee3deSMauro Carvalho Chehab 				goto err;
146525aee3deSMauro Carvalho Chehab 		}
146625aee3deSMauro Carvalho Chehab 	}
146725aee3deSMauro Carvalho Chehab 
146825aee3deSMauro Carvalho Chehab 	if (!dev->ci.en && (io & NGENE_IO_TSOUT))
146925aee3deSMauro Carvalho Chehab 		return 0;
147025aee3deSMauro Carvalho Chehab 
147125aee3deSMauro Carvalho Chehab 	if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
147225aee3deSMauro Carvalho Chehab 		if (nr >= STREAM_AUDIOIN1)
147325aee3deSMauro Carvalho Chehab 			chan->DataFormatFlags = DF_SWAP32;
147425aee3deSMauro Carvalho Chehab 
147525aee3deSMauro Carvalho Chehab 		if (nr == 0 || !one_adapter || dev->first_adapter == NULL) {
147625aee3deSMauro Carvalho Chehab 			adapter = &dev->adapter[nr];
147725aee3deSMauro Carvalho Chehab 			ret = dvb_register_adapter(adapter, "nGene",
147825aee3deSMauro Carvalho Chehab 						   THIS_MODULE,
147925aee3deSMauro Carvalho Chehab 						   &chan->dev->pci_dev->dev,
148025aee3deSMauro Carvalho Chehab 						   adapter_nr);
148125aee3deSMauro Carvalho Chehab 			if (ret < 0)
148225aee3deSMauro Carvalho Chehab 				goto err;
148325aee3deSMauro Carvalho Chehab 			if (dev->first_adapter == NULL)
148425aee3deSMauro Carvalho Chehab 				dev->first_adapter = adapter;
148525aee3deSMauro Carvalho Chehab 			chan->has_adapter = true;
148625aee3deSMauro Carvalho Chehab 		} else
148725aee3deSMauro Carvalho Chehab 			adapter = dev->first_adapter;
148825aee3deSMauro Carvalho Chehab 	}
148925aee3deSMauro Carvalho Chehab 
149025aee3deSMauro Carvalho Chehab 	if (dev->ci.en && (io & NGENE_IO_TSOUT)) {
1491f987b53cSAleksandr Burakov 		ret = dvb_ca_en50221_init(adapter, dev->ci.en, 0, 1);
1492f987b53cSAleksandr Burakov 		if (ret != 0)
1493f987b53cSAleksandr Burakov 			goto err;
149425aee3deSMauro Carvalho Chehab 		set_transfer(chan, 1);
149525aee3deSMauro Carvalho Chehab 		chan->dev->channel[2].DataFormatFlags = DF_SWAP32;
149625aee3deSMauro Carvalho Chehab 		set_transfer(&chan->dev->channel[2], 1);
149725aee3deSMauro Carvalho Chehab 		dvb_register_device(adapter, &chan->ci_dev,
149825aee3deSMauro Carvalho Chehab 				    &ngene_dvbdev_ci, (void *) chan,
1499df2f94e5SMauro Carvalho Chehab 				    DVB_DEVICE_SEC, 0);
150025aee3deSMauro Carvalho Chehab 		if (!chan->ci_dev)
150125aee3deSMauro Carvalho Chehab 			goto err;
150225aee3deSMauro Carvalho Chehab 	}
150325aee3deSMauro Carvalho Chehab 
150425aee3deSMauro Carvalho Chehab 	if (chan->fe) {
150525aee3deSMauro Carvalho Chehab 		if (dvb_register_frontend(adapter, chan->fe) < 0)
150625aee3deSMauro Carvalho Chehab 			goto err;
150725aee3deSMauro Carvalho Chehab 		chan->has_demux = true;
150825aee3deSMauro Carvalho Chehab 	}
150925aee3deSMauro Carvalho Chehab 	if (chan->fe2) {
151025aee3deSMauro Carvalho Chehab 		if (dvb_register_frontend(adapter, chan->fe2) < 0)
151125aee3deSMauro Carvalho Chehab 			goto err;
15125a4faee2SMauro Carvalho Chehab 		if (chan->fe) {
151325aee3deSMauro Carvalho Chehab 			chan->fe2->tuner_priv = chan->fe->tuner_priv;
151425aee3deSMauro Carvalho Chehab 			memcpy(&chan->fe2->ops.tuner_ops,
151525aee3deSMauro Carvalho Chehab 			       &chan->fe->ops.tuner_ops,
151625aee3deSMauro Carvalho Chehab 			       sizeof(struct dvb_tuner_ops));
151725aee3deSMauro Carvalho Chehab 		}
15185a4faee2SMauro Carvalho Chehab 	}
151925aee3deSMauro Carvalho Chehab 
152025aee3deSMauro Carvalho Chehab 	if (chan->has_demux) {
152125aee3deSMauro Carvalho Chehab 		ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux",
152225aee3deSMauro Carvalho Chehab 					      ngene_start_feed,
152325aee3deSMauro Carvalho Chehab 					      ngene_stop_feed, chan);
152425aee3deSMauro Carvalho Chehab 		ret = my_dvb_dmxdev_ts_card_init(&chan->dmxdev, &chan->demux,
152525aee3deSMauro Carvalho Chehab 						 &chan->hw_frontend,
152625aee3deSMauro Carvalho Chehab 						 &chan->mem_frontend, adapter);
152725aee3deSMauro Carvalho Chehab 		ret = dvb_net_init(adapter, &chan->dvbnet, &chan->demux.dmx);
152825aee3deSMauro Carvalho Chehab 	}
152925aee3deSMauro Carvalho Chehab 
153025aee3deSMauro Carvalho Chehab 	return ret;
153125aee3deSMauro Carvalho Chehab 
153225aee3deSMauro Carvalho Chehab err:
153325aee3deSMauro Carvalho Chehab 	if (chan->fe) {
153425aee3deSMauro Carvalho Chehab 		dvb_frontend_detach(chan->fe);
153525aee3deSMauro Carvalho Chehab 		chan->fe = NULL;
153625aee3deSMauro Carvalho Chehab 	}
153725aee3deSMauro Carvalho Chehab 	release_channel(chan);
153825aee3deSMauro Carvalho Chehab 	return 0;
153925aee3deSMauro Carvalho Chehab }
154025aee3deSMauro Carvalho Chehab 
init_channels(struct ngene * dev)154125aee3deSMauro Carvalho Chehab static int init_channels(struct ngene *dev)
154225aee3deSMauro Carvalho Chehab {
154325aee3deSMauro Carvalho Chehab 	int i, j;
154425aee3deSMauro Carvalho Chehab 
154525aee3deSMauro Carvalho Chehab 	for (i = 0; i < MAX_STREAM; i++) {
154625aee3deSMauro Carvalho Chehab 		dev->channel[i].number = i;
154725aee3deSMauro Carvalho Chehab 		if (init_channel(&dev->channel[i]) < 0) {
154825aee3deSMauro Carvalho Chehab 			for (j = i - 1; j >= 0; j--)
154925aee3deSMauro Carvalho Chehab 				release_channel(&dev->channel[j]);
155025aee3deSMauro Carvalho Chehab 			return -1;
155125aee3deSMauro Carvalho Chehab 		}
155225aee3deSMauro Carvalho Chehab 	}
155325aee3deSMauro Carvalho Chehab 	return 0;
155425aee3deSMauro Carvalho Chehab }
155525aee3deSMauro Carvalho Chehab 
1556d19e3a72SDaniel Scheller static const struct cxd2099_cfg cxd_cfgtmpl = {
155725aee3deSMauro Carvalho Chehab 	.bitrate = 62000,
155825aee3deSMauro Carvalho Chehab 	.polarity = 0,
155925aee3deSMauro Carvalho Chehab 	.clock_mode = 0,
156025aee3deSMauro Carvalho Chehab };
156125aee3deSMauro Carvalho Chehab 
cxd_attach(struct ngene * dev)156225aee3deSMauro Carvalho Chehab static void cxd_attach(struct ngene *dev)
156325aee3deSMauro Carvalho Chehab {
15646795bf62SDaniel Scheller 	struct device *pdev = &dev->pci_dev->dev;
156525aee3deSMauro Carvalho Chehab 	struct ngene_ci *ci = &dev->ci;
1566d19e3a72SDaniel Scheller 	struct cxd2099_cfg cxd_cfg = cxd_cfgtmpl;
1567d19e3a72SDaniel Scheller 	struct i2c_client *client;
1568e39b8e94SDaniel Scheller 	int ret;
1569e39b8e94SDaniel Scheller 	u8 type;
1570e39b8e94SDaniel Scheller 
1571e39b8e94SDaniel Scheller 	/* check for CXD2099AR presence before attaching */
1572e39b8e94SDaniel Scheller 	ret = ngene_port_has_cxd2099(&dev->channel[0].i2c_adapter, &type);
1573e39b8e94SDaniel Scheller 	if (!ret) {
1574e39b8e94SDaniel Scheller 		dev_dbg(pdev, "No CXD2099AR found\n");
1575e39b8e94SDaniel Scheller 		return;
1576e39b8e94SDaniel Scheller 	}
1577e39b8e94SDaniel Scheller 
1578e39b8e94SDaniel Scheller 	if (type != 1) {
1579e39b8e94SDaniel Scheller 		dev_warn(pdev, "CXD2099AR is uninitialized!\n");
1580e39b8e94SDaniel Scheller 		return;
1581e39b8e94SDaniel Scheller 	}
158225aee3deSMauro Carvalho Chehab 
1583d19e3a72SDaniel Scheller 	cxd_cfg.en = &ci->en;
158478c4e082SDaniel Scheller 	client = dvb_module_probe("cxd2099", NULL,
158578c4e082SDaniel Scheller 				  &dev->channel[0].i2c_adapter,
158678c4e082SDaniel Scheller 				  0x40, &cxd_cfg);
158778c4e082SDaniel Scheller 	if (!client)
158878c4e082SDaniel Scheller 		goto err;
1589d19e3a72SDaniel Scheller 
159025aee3deSMauro Carvalho Chehab 	ci->dev = dev;
1591d19e3a72SDaniel Scheller 	dev->channel[0].i2c_client[0] = client;
1592d19e3a72SDaniel Scheller 	return;
1593d19e3a72SDaniel Scheller 
159478c4e082SDaniel Scheller err:
15956795bf62SDaniel Scheller 	dev_err(pdev, "CXD2099AR attach failed\n");
159625aee3deSMauro Carvalho Chehab 	return;
159725aee3deSMauro Carvalho Chehab }
159825aee3deSMauro Carvalho Chehab 
cxd_detach(struct ngene * dev)159925aee3deSMauro Carvalho Chehab static void cxd_detach(struct ngene *dev)
160025aee3deSMauro Carvalho Chehab {
160125aee3deSMauro Carvalho Chehab 	struct ngene_ci *ci = &dev->ci;
160225aee3deSMauro Carvalho Chehab 
160325aee3deSMauro Carvalho Chehab 	dvb_ca_en50221_release(ci->en);
1604d19e3a72SDaniel Scheller 
160578c4e082SDaniel Scheller 	dvb_module_release(dev->channel[0].i2c_client[0]);
160678c4e082SDaniel Scheller 	dev->channel[0].i2c_client[0] = NULL;
1607c463c979SHans Verkuil 	ci->en = NULL;
160825aee3deSMauro Carvalho Chehab }
160925aee3deSMauro Carvalho Chehab 
161025aee3deSMauro Carvalho Chehab /***********************************/
161125aee3deSMauro Carvalho Chehab /* workaround for shutdown failure */
161225aee3deSMauro Carvalho Chehab /***********************************/
161325aee3deSMauro Carvalho Chehab 
ngene_unlink(struct ngene * dev)161425aee3deSMauro Carvalho Chehab static void ngene_unlink(struct ngene *dev)
161525aee3deSMauro Carvalho Chehab {
161625aee3deSMauro Carvalho Chehab 	struct ngene_command com;
161725aee3deSMauro Carvalho Chehab 
161825aee3deSMauro Carvalho Chehab 	com.cmd.hdr.Opcode = CMD_MEM_WRITE;
161925aee3deSMauro Carvalho Chehab 	com.cmd.hdr.Length = 3;
162025aee3deSMauro Carvalho Chehab 	com.cmd.MemoryWrite.address = 0x910c;
162125aee3deSMauro Carvalho Chehab 	com.cmd.MemoryWrite.data = 0xff;
162225aee3deSMauro Carvalho Chehab 	com.in_len = 3;
162325aee3deSMauro Carvalho Chehab 	com.out_len = 1;
162425aee3deSMauro Carvalho Chehab 
16251439cdb0SBinoy Jayan 	mutex_lock(&dev->cmd_mutex);
162625aee3deSMauro Carvalho Chehab 	ngwritel(0, NGENE_INT_ENABLE);
162725aee3deSMauro Carvalho Chehab 	ngene_command_mutex(dev, &com);
16281439cdb0SBinoy Jayan 	mutex_unlock(&dev->cmd_mutex);
162925aee3deSMauro Carvalho Chehab }
163025aee3deSMauro Carvalho Chehab 
ngene_shutdown(struct pci_dev * pdev)163125aee3deSMauro Carvalho Chehab void ngene_shutdown(struct pci_dev *pdev)
163225aee3deSMauro Carvalho Chehab {
16330e7e4d26SJingoo Han 	struct ngene *dev = pci_get_drvdata(pdev);
163425aee3deSMauro Carvalho Chehab 
163525aee3deSMauro Carvalho Chehab 	if (!dev || !shutdown_workaround)
163625aee3deSMauro Carvalho Chehab 		return;
163725aee3deSMauro Carvalho Chehab 
16386795bf62SDaniel Scheller 	dev_info(&pdev->dev, "shutdown workaround...\n");
163925aee3deSMauro Carvalho Chehab 	ngene_unlink(dev);
164025aee3deSMauro Carvalho Chehab 	pci_disable_device(pdev);
164125aee3deSMauro Carvalho Chehab }
164225aee3deSMauro Carvalho Chehab 
164325aee3deSMauro Carvalho Chehab /****************************************************************************/
164425aee3deSMauro Carvalho Chehab /* device probe/remove calls ************************************************/
164525aee3deSMauro Carvalho Chehab /****************************************************************************/
164625aee3deSMauro Carvalho Chehab 
ngene_remove(struct pci_dev * pdev)16474c62e976SGreg Kroah-Hartman void ngene_remove(struct pci_dev *pdev)
164825aee3deSMauro Carvalho Chehab {
164925aee3deSMauro Carvalho Chehab 	struct ngene *dev = pci_get_drvdata(pdev);
165025aee3deSMauro Carvalho Chehab 	int i;
165125aee3deSMauro Carvalho Chehab 
165225aee3deSMauro Carvalho Chehab 	tasklet_kill(&dev->event_tasklet);
165325aee3deSMauro Carvalho Chehab 	for (i = MAX_STREAM - 1; i >= 0; i--)
165425aee3deSMauro Carvalho Chehab 		release_channel(&dev->channel[i]);
165525aee3deSMauro Carvalho Chehab 	if (dev->ci.en)
165625aee3deSMauro Carvalho Chehab 		cxd_detach(dev);
165725aee3deSMauro Carvalho Chehab 	ngene_stop(dev);
165825aee3deSMauro Carvalho Chehab 	ngene_release_buffers(dev);
165925aee3deSMauro Carvalho Chehab 	pci_disable_device(pdev);
166025aee3deSMauro Carvalho Chehab }
166125aee3deSMauro Carvalho Chehab 
ngene_probe(struct pci_dev * pci_dev,const struct pci_device_id * id)16624c62e976SGreg Kroah-Hartman int ngene_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
166325aee3deSMauro Carvalho Chehab {
166425aee3deSMauro Carvalho Chehab 	struct ngene *dev;
166525aee3deSMauro Carvalho Chehab 	int stat = 0;
166625aee3deSMauro Carvalho Chehab 
166725aee3deSMauro Carvalho Chehab 	if (pci_enable_device(pci_dev) < 0)
166825aee3deSMauro Carvalho Chehab 		return -ENODEV;
166925aee3deSMauro Carvalho Chehab 
167025aee3deSMauro Carvalho Chehab 	dev = vzalloc(sizeof(struct ngene));
167125aee3deSMauro Carvalho Chehab 	if (dev == NULL) {
167225aee3deSMauro Carvalho Chehab 		stat = -ENOMEM;
167325aee3deSMauro Carvalho Chehab 		goto fail0;
167425aee3deSMauro Carvalho Chehab 	}
167525aee3deSMauro Carvalho Chehab 
167625aee3deSMauro Carvalho Chehab 	dev->pci_dev = pci_dev;
167725aee3deSMauro Carvalho Chehab 	dev->card_info = (struct ngene_info *)id->driver_data;
16786795bf62SDaniel Scheller 	dev_info(&pci_dev->dev, "Found %s\n", dev->card_info->name);
167925aee3deSMauro Carvalho Chehab 
168025aee3deSMauro Carvalho Chehab 	pci_set_drvdata(pci_dev, dev);
168125aee3deSMauro Carvalho Chehab 
168225aee3deSMauro Carvalho Chehab 	/* Alloc buffers and start nGene */
168325aee3deSMauro Carvalho Chehab 	stat = ngene_get_buffers(dev);
168425aee3deSMauro Carvalho Chehab 	if (stat < 0)
168525aee3deSMauro Carvalho Chehab 		goto fail1;
168625aee3deSMauro Carvalho Chehab 	stat = ngene_start(dev);
168725aee3deSMauro Carvalho Chehab 	if (stat < 0)
168825aee3deSMauro Carvalho Chehab 		goto fail1;
168925aee3deSMauro Carvalho Chehab 
169025aee3deSMauro Carvalho Chehab 	cxd_attach(dev);
169125aee3deSMauro Carvalho Chehab 
169225aee3deSMauro Carvalho Chehab 	stat = ngene_buffer_config(dev);
169325aee3deSMauro Carvalho Chehab 	if (stat < 0)
169425aee3deSMauro Carvalho Chehab 		goto fail1;
169525aee3deSMauro Carvalho Chehab 
169625aee3deSMauro Carvalho Chehab 
169725aee3deSMauro Carvalho Chehab 	dev->i2c_current_bus = -1;
169825aee3deSMauro Carvalho Chehab 
169925aee3deSMauro Carvalho Chehab 	/* Register DVB adapters and devices for both channels */
1700bd6973eaSPeter Senna Tschudin 	stat = init_channels(dev);
1701bd6973eaSPeter Senna Tschudin 	if (stat < 0)
170225aee3deSMauro Carvalho Chehab 		goto fail2;
170325aee3deSMauro Carvalho Chehab 
170425aee3deSMauro Carvalho Chehab 	return 0;
170525aee3deSMauro Carvalho Chehab 
170625aee3deSMauro Carvalho Chehab fail2:
170725aee3deSMauro Carvalho Chehab 	ngene_stop(dev);
170825aee3deSMauro Carvalho Chehab fail1:
170925aee3deSMauro Carvalho Chehab 	ngene_release_buffers(dev);
171025aee3deSMauro Carvalho Chehab fail0:
171125aee3deSMauro Carvalho Chehab 	pci_disable_device(pci_dev);
171225aee3deSMauro Carvalho Chehab 	return stat;
171325aee3deSMauro Carvalho Chehab }
1714