linux/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c
Linus Torvalds b037865754 media updates for v4.4-rc1
-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJWOJZoAAoJEAhfPr2O5OEVmjYP/0RnfVjvRDtx0RxHDmvsowlt
 sHyrm5C7VME06b4J3O9qpC7PbMCAalvSkYp+bbxF+b//9EfwjvRER+NR8ebgn1Mw
 1NQKMtCusWRf4RzI+9osB3pFYgg/cYG2nKl0QVCXHL6xZszEQ9dBrFHEEHfVe8db
 JU1fGuF6TQNJdYgsVNMN9rStRB0vj3urfehLjB+E138VzDAnzPNA7I7Z4xsWWJw3
 V+J7CWLN1xW9IT59LXtRjbD/aCF9KrAmGigS0nCtDz7XVRPh+ZoXQLD073uLP2L3
 uYxOmadvc5+5iVwUP4zSsJ6+vw9kLr6Q30sNtLP7V+VkCSlCQNTOePLavB5T8qVY
 M2qALvwWjujtoSEjZHr7TqrlEpio98OSy1dNJ8GmuOb3UUAKocNN8sGG8h2nR/BR
 wv2OL/XPNcyB2LV6HeHZz9JiXB+rTbyXEN8CP2cD8ruGhNM5haak3d2l4FYszRXr
 /a/5JlYAcNrJii6PAXHyBtm6l0C4GPiAk3HQhII2fTErRr8fpln/G5AfaKjun5H8
 1Rbxx5JP+5qSHozmz2hNb4w92qqtPugj7qqu7sHCbwKLhh2Aspwo12GkN9acOIsI
 Kn1U/DWMRrkyptJAxBihsrEX3BXeQdNOPydKfMYEM7qE8EfTDM0uaIFQ+KVWCmNA
 Qh2TXAp6CZiuBvaqKzyl
 =sR0p
 -----END PGP SIGNATURE-----

Merge tag 'media/v4.4-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media

Pull media updates from Mauro Carvalho Chehab:
 "Media updates, including:

   - Lots of improvements at the kABI documentation
   - Split of Videobuf2 into a common part and a V4L2 specific one
   - Split of the VB2 tracing events into a separate header file
   - s5p-mfc got support for Exynos 5433
   - v4l2 fixes for 64-bits alignment when running 32 bits userspace
     on ARM
   - Added support for SDR radio transmitter at core, vivid and hackrf
     drivers
   - Some y2038 fixups
   - Some improvements at V4L2 colorspace support
   - saa7164 converted to use the V4L2 core control framework
   - several new boards additions, cleanups and fixups

  PS: There are two patches for scripts/kernel-doc that are needed by
  the documentation patches on Media.  Jon is OK on merging those via
  my tree"

* tag 'media/v4.4-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (146 commits)
  [media] c8sectpfe: Remove select on CONFIG_FW_LOADER_USER_HELPER_FALLBACK
  [media] DocBook media: update copyright/version numbers
  [media] ivtv: Convert to get_user_pages_unlocked()
  [media] media/v4l2-ctrls: fix setting autocluster to manual with VIDIOC_S_CTRL
  [media] DocBook media: Fix a typo in encoder cmd
  [media] DocBook: add SDR specific info to G_MODULATOR / S_MODULATOR
  [media] DocBook: add SDR specific info to G_TUNER / S_TUNER
  [media] hackrf: do not set human readable name for formats
  [media] hackrf: add support for transmitter
  [media] hackrf: switch to single function which configures everything
  [media] hackrf: add control for RF amplifier
  [media] DocBook: add modulator type field
  [media] v4l: add type field to v4l2_modulator struct
  [media] DocBook: document SDR transmitter
  [media] v4l2: add support for SDR transmitter
  [media] DocBook: document tuner RF gain control
  [media] v4l2: add RF gain control
  [media] v4l2: rename V4L2_TUNER_ADC to V4L2_TUNER_SDR
  [media] media/vivid-osd: fix info leak in ioctl
  [media] media: videobuf2: Move v4l2-specific stuff to videobuf2-v4l2
  ...
2015-11-05 12:05:15 -08:00

249 lines
6.4 KiB
C

/*
* netup_unidvb_spi.c
*
* Internal SPI driver for NetUP Universal Dual DVB-CI
*
* Copyright (C) 2014 NetUP Inc.
* Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
* Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "netup_unidvb.h"
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
#include <linux/mtd/partitions.h>
#include <mtd/mtd-abi.h>
#define NETUP_SPI_CTRL_IRQ 0x1000
#define NETUP_SPI_CTRL_IMASK 0x2000
#define NETUP_SPI_CTRL_START 0x8000
#define NETUP_SPI_CTRL_LAST_CS 0x4000
#define NETUP_SPI_TIMEOUT 6000
enum netup_spi_state {
SPI_STATE_START,
SPI_STATE_DONE,
};
struct netup_spi_regs {
__u8 data[1024];
__le16 control_stat;
__le16 clock_divider;
} __packed __aligned(1);
struct netup_spi {
struct device *dev;
struct spi_master *master;
struct netup_spi_regs __iomem *regs;
u8 __iomem *mmio;
spinlock_t lock;
wait_queue_head_t waitq;
enum netup_spi_state state;
};
static char netup_spi_name[64] = "fpga";
static struct mtd_partition netup_spi_flash_partitions = {
.name = netup_spi_name,
.size = 0x1000000, /* 16MB */
.offset = 0,
.mask_flags = MTD_CAP_ROM
};
static struct flash_platform_data spi_flash_data = {
.name = "netup0_m25p128",
.parts = &netup_spi_flash_partitions,
.nr_parts = 1,
};
static struct spi_board_info netup_spi_board = {
.modalias = "m25p128",
.max_speed_hz = 11000000,
.chip_select = 0,
.mode = SPI_MODE_0,
.platform_data = &spi_flash_data,
};
irqreturn_t netup_spi_interrupt(struct netup_spi *spi)
{
u16 reg;
unsigned long flags;
if (!spi)
return IRQ_NONE;
spin_lock_irqsave(&spi->lock, flags);
reg = readw(&spi->regs->control_stat);
if (!(reg & NETUP_SPI_CTRL_IRQ)) {
spin_unlock_irqrestore(&spi->lock, flags);
dev_dbg(&spi->master->dev,
"%s(): not mine interrupt\n", __func__);
return IRQ_NONE;
}
writew(reg | NETUP_SPI_CTRL_IRQ, &spi->regs->control_stat);
reg = readw(&spi->regs->control_stat);
writew(reg & ~NETUP_SPI_CTRL_IMASK, &spi->regs->control_stat);
spi->state = SPI_STATE_DONE;
wake_up(&spi->waitq);
spin_unlock_irqrestore(&spi->lock, flags);
dev_dbg(&spi->master->dev,
"%s(): SPI interrupt handled\n", __func__);
return IRQ_HANDLED;
}
static int netup_spi_transfer(struct spi_master *master,
struct spi_message *msg)
{
struct netup_spi *spi = spi_master_get_devdata(master);
struct spi_transfer *t;
int result = 0;
u32 tr_size;
/* reset CS */
writew(NETUP_SPI_CTRL_LAST_CS, &spi->regs->control_stat);
writew(0, &spi->regs->control_stat);
list_for_each_entry(t, &msg->transfers, transfer_list) {
tr_size = t->len;
while (tr_size) {
u32 frag_offset = t->len - tr_size;
u32 frag_size = (tr_size > sizeof(spi->regs->data)) ?
sizeof(spi->regs->data) : tr_size;
int frag_last = 0;
if (list_is_last(&t->transfer_list,
&msg->transfers) &&
frag_offset + frag_size == t->len) {
frag_last = 1;
}
if (t->tx_buf) {
memcpy_toio(spi->regs->data,
t->tx_buf + frag_offset,
frag_size);
} else {
memset_io(spi->regs->data,
0, frag_size);
}
spi->state = SPI_STATE_START;
writew((frag_size & 0x3ff) |
NETUP_SPI_CTRL_IMASK |
NETUP_SPI_CTRL_START |
(frag_last ? NETUP_SPI_CTRL_LAST_CS : 0),
&spi->regs->control_stat);
dev_dbg(&spi->master->dev,
"%s(): control_stat 0x%04x\n",
__func__, readw(&spi->regs->control_stat));
wait_event_timeout(spi->waitq,
spi->state != SPI_STATE_START,
msecs_to_jiffies(NETUP_SPI_TIMEOUT));
if (spi->state == SPI_STATE_DONE) {
if (t->rx_buf) {
memcpy_fromio(t->rx_buf + frag_offset,
spi->regs->data, frag_size);
}
} else {
if (spi->state == SPI_STATE_START) {
dev_dbg(&spi->master->dev,
"%s(): transfer timeout\n",
__func__);
} else {
dev_dbg(&spi->master->dev,
"%s(): invalid state %d\n",
__func__, spi->state);
}
result = -EIO;
goto done;
}
tr_size -= frag_size;
msg->actual_length += frag_size;
}
}
done:
msg->status = result;
spi_finalize_current_message(master);
return result;
}
static int netup_spi_setup(struct spi_device *spi)
{
return 0;
}
int netup_spi_init(struct netup_unidvb_dev *ndev)
{
struct spi_master *master;
struct netup_spi *nspi;
master = spi_alloc_master(&ndev->pci_dev->dev,
sizeof(struct netup_spi));
if (!master) {
dev_err(&ndev->pci_dev->dev,
"%s(): unable to alloc SPI master\n", __func__);
return -EINVAL;
}
nspi = spi_master_get_devdata(master);
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
master->bus_num = -1;
master->num_chipselect = 1;
master->transfer_one_message = netup_spi_transfer;
master->setup = netup_spi_setup;
spin_lock_init(&nspi->lock);
init_waitqueue_head(&nspi->waitq);
nspi->master = master;
nspi->regs = (struct netup_spi_regs __iomem *)(ndev->bmmio0 + 0x4000);
writew(2, &nspi->regs->clock_divider);
writew(NETUP_UNIDVB_IRQ_SPI, ndev->bmmio0 + REG_IMASK_SET);
ndev->spi = nspi;
if (spi_register_master(master)) {
ndev->spi = NULL;
dev_err(&ndev->pci_dev->dev,
"%s(): unable to register SPI bus\n", __func__);
return -EINVAL;
}
snprintf(netup_spi_name,
sizeof(netup_spi_name),
"fpga_%02x:%02x.%01x",
ndev->pci_bus,
ndev->pci_slot,
ndev->pci_func);
if (!spi_new_device(master, &netup_spi_board)) {
ndev->spi = NULL;
dev_err(&ndev->pci_dev->dev,
"%s(): unable to create SPI device\n", __func__);
return -EINVAL;
}
dev_dbg(&ndev->pci_dev->dev, "%s(): SPI init OK\n", __func__);
return 0;
}
void netup_spi_release(struct netup_unidvb_dev *ndev)
{
u16 reg;
unsigned long flags;
struct netup_spi *spi = ndev->spi;
if (!spi)
return;
spin_lock_irqsave(&spi->lock, flags);
reg = readw(&spi->regs->control_stat);
writew(reg | NETUP_SPI_CTRL_IRQ, &spi->regs->control_stat);
reg = readw(&spi->regs->control_stat);
writew(reg & ~NETUP_SPI_CTRL_IMASK, &spi->regs->control_stat);
spin_unlock_irqrestore(&spi->lock, flags);
spi_unregister_master(spi->master);
ndev->spi = NULL;
}