// SPDX-License-Identifier: GPL-2.0
/*
* c8sectpfe-core.c - C8SECTPFE STi DVB driver
*
* Copyright (c) STMicroelectronics 2015
*
* Author:Peter Bennett <peter.bennett@st.com>
* Peter Griffin <peter.griffin@linaro.org>
*
*/
#include <linux/atomic.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/dvb/dmx.h>
#include <linux/dvb/frontend.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/firmware.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/usb.h>
#include <linux/wait.h>
#include "c8sectpfe-common.h"
#include "c8sectpfe-core.h"
#include "c8sectpfe-debugfs.h"
#include <media/dmxdev.h>
#include <media/dvb_demux.h>
#include <media/dvb_frontend.h>
#include <media/dvb_net.h>
#define FIRMWARE_MEMDMA "pti_memdma_h407.elf"
MODULE_FIRMWARE(FIRMWARE_MEMDMA);
#define PID_TABLE_SIZE 1024
#define POLL_MSECS 50
static int load_c8sectpfe_fw(struct c8sectpfei *fei);
#define TS_PKT_SIZE 188
#define HEADER_SIZE (4)
#define PACKET_SIZE (TS_PKT_SIZE+HEADER_SIZE)
#define FEI_ALIGNMENT (32)
/* hw requires minimum of 8*PACKET_SIZE and padded to 8byte boundary */
#define FEI_BUFFER_SIZE (8*PACKET_SIZE*340)
#define FIFO_LEN 1024
static void c8sectpfe_timer_interrupt(struct timer_list *t)
{
struct c8sectpfei *fei = from_timer(fei, t, timer);
struct channel_info *channel;
int chan_num;
/* iterate through input block channels */
for (chan_num = 0; chan_num < fei->tsin_count; chan_num++) {
channel = fei->channel_data[chan_num];
/* is this descriptor initialised and TP enabled */
if (channel->irec && readl(channel->irec + DMA_PRDS_TPENABLE))
queue_work(system_bh_wq, &channel->bh_work);
}
fei->timer.expires = jiffies + msecs_to_jiffies(POLL_MSECS);
add_timer(&fei->timer);
}
static void channel_swdemux_bh_work(struct work_struct *t)
{
struct channel_info *channel = from_work(channel, t, bh_work);
struct c8sectpfei *fei;
unsigned long wp, rp;
int pos, num_packets, n, size;
u8 *buf;
if (unlikely(!channel || !channel->irec))
return;
fei = channel->fei;
wp = readl(channel->irec + DMA_PRDS_BUSWP_TP(0));
rp = readl(channel->irec + DMA_PRDS_BUSRP_TP(0));
pos = rp - channel->back_buffer_busaddr;
/* has it wrapped */
if (wp < rp)
wp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE;
size = wp - rp;
num_packets = size / PACKET_SIZE;
/* manage cache so data is visible to CPU */
dma_sync_single_for_cpu(fei->dev,
rp,
size,
DMA_FROM_DEVICE);
buf = channel->back_buffer_aligned;
dev_dbg(fei->dev,
"chan=%d channel=%p num_packets = %d, buf = %p, pos = 0x%x\n\trp=0x%lx, wp=0x%lx\n",
channel->tsin_id, channel, num_packets, buf, pos, rp, wp);
for (n = 0; n < num_packets; n++) {
dvb_dmx_swfilter_packets(
&fei->c8sectpfe[0]->
demux[channel->demux_mapping].dvb_demux,
&buf[pos], 1);
pos += PACKET_SIZE;
}
/* advance the read pointer */
if (wp == (channel->back_buffer_busaddr + FEI_BUFFER_SIZE))
writel(channel->back_buffer_busaddr, channel->irec +
DMA_PRDS_BUSRP_TP(0));
else
writel(wp, channel->irec + DMA_PRDS_BUSRP_TP(0));
}
static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed)
{
struct dvb_demux *demux = dvbdmxfeed->demux;
struct stdemux *stdemux = demux->priv;
struct c8sectpfei *fei = stdemux->c8sectpfei;
struct channel_info *channel;
u32