mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 22:02:28 +00:00
bcb63314e2
Drop the FSF's postal address from the source code files that typically contain mostly the license text. Of the 628 removed instances, 578 are outdated. The patch has been created with the following command without manual edits: git grep -l "675 Mass Ave\|59 Temple Place\|51 Franklin St" -- \ drivers/media/ include/media|while read i; do i=$i perl -e ' open(F,"< $ENV{i}"); $a=join("", <F>); $a =~ s/[ \t]*\*\n.*You should.*\n.*along with.*\n.*(\n.*USA.*$)?\n//m && $a =~ s/(^.*)Or, (point your browser to) /$1To obtain the license, $2\n$1/m; close(F); open(F, "> $ENV{i}"); print F $a; close(F);'; done Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
363 lines
8.6 KiB
C
363 lines
8.6 KiB
C
/*
|
|
* ppi.c Analog Devices Parallel Peripheral Interface driver
|
|
*
|
|
* Copyright (c) 2011 Analog Devices Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* 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 <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <asm/bfin_ppi.h>
|
|
#include <asm/blackfin.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/dma.h>
|
|
#include <asm/portmux.h>
|
|
|
|
#include <media/blackfin/ppi.h>
|
|
|
|
static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler);
|
|
static void ppi_detach_irq(struct ppi_if *ppi);
|
|
static int ppi_start(struct ppi_if *ppi);
|
|
static int ppi_stop(struct ppi_if *ppi);
|
|
static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params);
|
|
static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr);
|
|
|
|
static const struct ppi_ops ppi_ops = {
|
|
.attach_irq = ppi_attach_irq,
|
|
.detach_irq = ppi_detach_irq,
|
|
.start = ppi_start,
|
|
.stop = ppi_stop,
|
|
.set_params = ppi_set_params,
|
|
.update_addr = ppi_update_addr,
|
|
};
|
|
|
|
static irqreturn_t ppi_irq_err(int irq, void *dev_id)
|
|
{
|
|
struct ppi_if *ppi = dev_id;
|
|
const struct ppi_info *info = ppi->info;
|
|
|
|
switch (info->type) {
|
|
case PPI_TYPE_PPI:
|
|
{
|
|
struct bfin_ppi_regs *reg = info->base;
|
|
unsigned short status;
|
|
|
|
/* register on bf561 is cleared when read
|
|
* others are W1C
|
|
*/
|
|
status = bfin_read16(®->status);
|
|
if (status & 0x3000)
|
|
ppi->err = true;
|
|
bfin_write16(®->status, 0xff00);
|
|
break;
|
|
}
|
|
case PPI_TYPE_EPPI:
|
|
{
|
|
struct bfin_eppi_regs *reg = info->base;
|
|
unsigned short status;
|
|
|
|
status = bfin_read16(®->status);
|
|
if (status & 0x2)
|
|
ppi->err = true;
|
|
bfin_write16(®->status, 0xffff);
|
|
break;
|
|
}
|
|
case PPI_TYPE_EPPI3:
|
|
{
|
|
struct bfin_eppi3_regs *reg = info->base;
|
|
unsigned long stat;
|
|
|
|
stat = bfin_read32(®->stat);
|
|
if (stat & 0x2)
|
|
ppi->err = true;
|
|
bfin_write32(®->stat, 0xc0ff);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler)
|
|
{
|
|
const struct ppi_info *info = ppi->info;
|
|
int ret;
|
|
|
|
ret = request_dma(info->dma_ch, "PPI_DMA");
|
|
|
|
if (ret) {
|
|
pr_err("Unable to allocate DMA channel for PPI\n");
|
|
return ret;
|
|
}
|
|
set_dma_callback(info->dma_ch, handler, ppi);
|
|
|
|
if (ppi->err_int) {
|
|
ret = request_irq(info->irq_err, ppi_irq_err, 0, "PPI ERROR", ppi);
|
|
if (ret) {
|
|
pr_err("Unable to allocate IRQ for PPI\n");
|
|
free_dma(info->dma_ch);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void ppi_detach_irq(struct ppi_if *ppi)
|
|
{
|
|
const struct ppi_info *info = ppi->info;
|
|
|
|
if (ppi->err_int)
|
|
free_irq(info->irq_err, ppi);
|
|
free_dma(info->dma_ch);
|
|
}
|
|
|
|
static int ppi_start(struct ppi_if *ppi)
|
|
{
|
|
const struct ppi_info *info = ppi->info;
|
|
|
|
/* enable DMA */
|
|
enable_dma(info->dma_ch);
|
|
|
|
/* enable PPI */
|
|
ppi->ppi_control |= PORT_EN;
|
|
switch (info->type) {
|
|
case PPI_TYPE_PPI:
|
|
{
|
|
struct bfin_ppi_regs *reg = info->base;
|
|
bfin_write16(®->control, ppi->ppi_control);
|
|
break;
|
|
}
|
|
case PPI_TYPE_EPPI:
|
|
{
|
|
struct bfin_eppi_regs *reg = info->base;
|
|
bfin_write32(®->control, ppi->ppi_control);
|
|
break;
|
|
}
|
|
case PPI_TYPE_EPPI3:
|
|
{
|
|
struct bfin_eppi3_regs *reg = info->base;
|
|
bfin_write32(®->ctl, ppi->ppi_control);
|
|
break;
|
|
}
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
SSYNC();
|
|
return 0;
|
|
}
|
|
|
|
static int ppi_stop(struct ppi_if *ppi)
|
|
{
|
|
const struct ppi_info *info = ppi->info;
|
|
|
|
/* disable PPI */
|
|
ppi->ppi_control &= ~PORT_EN;
|
|
switch (info->type) {
|
|
case PPI_TYPE_PPI:
|
|
{
|
|
struct bfin_ppi_regs *reg = info->base;
|
|
bfin_write16(®->control, ppi->ppi_control);
|
|
break;
|
|
}
|
|
case PPI_TYPE_EPPI:
|
|
{
|
|
struct bfin_eppi_regs *reg = info->base;
|
|
bfin_write32(®->control, ppi->ppi_control);
|
|
break;
|
|
}
|
|
case PPI_TYPE_EPPI3:
|
|
{
|
|
struct bfin_eppi3_regs *reg = info->base;
|
|
bfin_write32(®->ctl, ppi->ppi_control);
|
|
break;
|
|
}
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* disable DMA */
|
|
clear_dma_irqstat(info->dma_ch);
|
|
disable_dma(info->dma_ch);
|
|
|
|
SSYNC();
|
|
return 0;
|
|
}
|
|
|
|
static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params)
|
|
{
|
|
const struct ppi_info *info = ppi->info;
|
|
int dma32 = 0;
|
|
int dma_config, bytes_per_line;
|
|
int hcount, hdelay, samples_per_line;
|
|
|
|
#ifdef CONFIG_PINCTRL
|
|
static const char * const pin_state[] = {"8bit", "16bit", "24bit"};
|
|
struct pinctrl *pctrl;
|
|
struct pinctrl_state *pstate;
|
|
|
|
if (params->dlen > 24 || params->dlen <= 0)
|
|
return -EINVAL;
|
|
pctrl = devm_pinctrl_get(ppi->dev);
|
|
if (IS_ERR(pctrl))
|
|
return PTR_ERR(pctrl);
|
|
pstate = pinctrl_lookup_state(pctrl,
|
|
pin_state[(params->dlen + 7) / 8 - 1]);
|
|
if (pinctrl_select_state(pctrl, pstate))
|
|
return -EINVAL;
|
|
#endif
|
|
|
|
bytes_per_line = params->width * params->bpp / 8;
|
|
/* convert parameters unit from pixels to samples */
|
|
hcount = params->width * params->bpp / params->dlen;
|
|
hdelay = params->hdelay * params->bpp / params->dlen;
|
|
samples_per_line = params->line * params->bpp / params->dlen;
|
|
if (params->int_mask == 0xFFFFFFFF)
|
|
ppi->err_int = false;
|
|
else
|
|
ppi->err_int = true;
|
|
|
|
dma_config = (DMA_FLOW_STOP | RESTART | DMA2D | DI_EN_Y);
|
|
ppi->ppi_control = params->ppi_control & ~PORT_EN;
|
|
if (!(ppi->ppi_control & PORT_DIR))
|
|
dma_config |= WNR;
|
|
switch (info->type) {
|
|
case PPI_TYPE_PPI:
|
|
{
|
|
struct bfin_ppi_regs *reg = info->base;
|
|
|
|
if (params->ppi_control & DMA32)
|
|
dma32 = 1;
|
|
|
|
bfin_write16(®->control, ppi->ppi_control);
|
|
bfin_write16(®->count, samples_per_line - 1);
|
|
bfin_write16(®->frame, params->frame);
|
|
break;
|
|
}
|
|
case PPI_TYPE_EPPI:
|
|
{
|
|
struct bfin_eppi_regs *reg = info->base;
|
|
|
|
if ((params->ppi_control & PACK_EN)
|
|
|| (params->ppi_control & 0x38000) > DLEN_16)
|
|
dma32 = 1;
|
|
|
|
bfin_write32(®->control, ppi->ppi_control);
|
|
bfin_write16(®->line, samples_per_line);
|
|
bfin_write16(®->frame, params->frame);
|
|
bfin_write16(®->hdelay, hdelay);
|
|
bfin_write16(®->vdelay, params->vdelay);
|
|
bfin_write16(®->hcount, hcount);
|
|
bfin_write16(®->vcount, params->height);
|
|
break;
|
|
}
|
|
case PPI_TYPE_EPPI3:
|
|
{
|
|
struct bfin_eppi3_regs *reg = info->base;
|
|
|
|
if ((params->ppi_control & PACK_EN)
|
|
|| (params->ppi_control & 0x70000) > DLEN_16)
|
|
dma32 = 1;
|
|
|
|
bfin_write32(®->ctl, ppi->ppi_control);
|
|
bfin_write32(®->line, samples_per_line);
|
|
bfin_write32(®->frame, params->frame);
|
|
bfin_write32(®->hdly, hdelay);
|
|
bfin_write32(®->vdly, params->vdelay);
|
|
bfin_write32(®->hcnt, hcount);
|
|
bfin_write32(®->vcnt, params->height);
|
|
if (params->int_mask)
|
|
bfin_write32(®->imsk, params->int_mask & 0xFF);
|
|
if (ppi->ppi_control & PORT_DIR) {
|
|
u32 hsync_width, vsync_width, vsync_period;
|
|
|
|
hsync_width = params->hsync
|
|
* params->bpp / params->dlen;
|
|
vsync_width = params->vsync * samples_per_line;
|
|
vsync_period = samples_per_line * params->frame;
|
|
bfin_write32(®->fs1_wlhb, hsync_width);
|
|
bfin_write32(®->fs1_paspl, samples_per_line);
|
|
bfin_write32(®->fs2_wlvb, vsync_width);
|
|
bfin_write32(®->fs2_palpf, vsync_period);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (dma32) {
|
|
dma_config |= WDSIZE_32 | PSIZE_32;
|
|
set_dma_x_count(info->dma_ch, bytes_per_line >> 2);
|
|
set_dma_x_modify(info->dma_ch, 4);
|
|
set_dma_y_modify(info->dma_ch, 4);
|
|
} else {
|
|
dma_config |= WDSIZE_16 | PSIZE_16;
|
|
set_dma_x_count(info->dma_ch, bytes_per_line >> 1);
|
|
set_dma_x_modify(info->dma_ch, 2);
|
|
set_dma_y_modify(info->dma_ch, 2);
|
|
}
|
|
set_dma_y_count(info->dma_ch, params->height);
|
|
set_dma_config(info->dma_ch, dma_config);
|
|
|
|
SSYNC();
|
|
return 0;
|
|
}
|
|
|
|
static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr)
|
|
{
|
|
set_dma_start_addr(ppi->info->dma_ch, addr);
|
|
}
|
|
|
|
struct ppi_if *ppi_create_instance(struct platform_device *pdev,
|
|
const struct ppi_info *info)
|
|
{
|
|
struct ppi_if *ppi;
|
|
|
|
if (!info || !info->pin_req)
|
|
return NULL;
|
|
|
|
#ifndef CONFIG_PINCTRL
|
|
if (peripheral_request_list(info->pin_req, KBUILD_MODNAME)) {
|
|
dev_err(&pdev->dev, "request peripheral failed\n");
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
ppi = kzalloc(sizeof(*ppi), GFP_KERNEL);
|
|
if (!ppi) {
|
|
peripheral_free_list(info->pin_req);
|
|
dev_err(&pdev->dev, "unable to allocate memory for ppi handle\n");
|
|
return NULL;
|
|
}
|
|
ppi->ops = &ppi_ops;
|
|
ppi->info = info;
|
|
ppi->dev = &pdev->dev;
|
|
|
|
pr_info("ppi probe success\n");
|
|
return ppi;
|
|
}
|
|
EXPORT_SYMBOL(ppi_create_instance);
|
|
|
|
void ppi_delete_instance(struct ppi_if *ppi)
|
|
{
|
|
peripheral_free_list(ppi->info->pin_req);
|
|
kfree(ppi);
|
|
}
|
|
EXPORT_SYMBOL(ppi_delete_instance);
|
|
|
|
MODULE_DESCRIPTION("Analog Devices PPI driver");
|
|
MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
|
|
MODULE_LICENSE("GPL v2");
|