blob: 476f83d8b0d0e2eb5d3d208cb267e90aa791ffbd [file] [log] [blame]
/**
* linux/driver/parrot/uio/hx280-venc.c - On2 / Hantro user I/O video encoder
* implementation
*
* Copyright (C) 2011 Parrot S.A.
*
* author: Gregor Boirie <gregor.boirie@parrot.com>
* date: 31-Aug-2011
*
* This file is released under the GPL
*/
#include <linux/module.h>
#include <linux/io.h>
#include <linux/sched.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "hx-uio.h"
#include "hx280-venc.h"
#define DRV_VERSION "0.1"
#define HX280_PRODUCT_ID_R1 (0x72800000U)
#define HX280_PRODUCT_ID_R2 (0x48310000U)
#define HX280_REGS_NR 14
#define HX280_ID 0x0
#define HX280_ID_PRODUCT_MSK (0xffff0000U)
#define HX280_IRQ 0x4
#define HX280_IRQ_HINTENC (1U << 0)
#define HX280_IRQ_INTDISABLE (1U << 1)
#define HX280_IRQ_SLICE_STATUS (1U << 8)
#define HX280_COMCTRL 0x38
static int hx280_handle_irq(struct hx_device const* hxdev)
{
unsigned long const reg = hxdev->regs_virt + HX280_IRQ;
u32 const status = readl_relaxed(reg);
if (unlikely(! (status & HX280_IRQ_HINTENC)))
/* Spurious ? */
return 0;
writel_relaxed(status & ~(HX280_IRQ_SLICE_STATUS | HX280_IRQ_HINTENC),
reg);
return 1;
}
static void hx280_setup_irq(struct hx_device const* hxdev, int command)
{
unsigned long const reg = hxdev->regs_virt + HX280_IRQ;
u32 const word = readl(reg);
if (! command)
/* Disable interrupts generation. */
writel_relaxed(word | HX280_IRQ_INTDISABLE, reg);
else
/* Enable interrupts generature. */
writel_relaxed(word & ~HX280_IRQ_INTDISABLE, reg);
}
static int __devinit hx280_init_hw(struct hx_device const* hxdev)
{
unsigned long const regs = hxdev->regs_virt;
u32 const id = readl(regs + HX280_ID);
unsigned int r;
/* Check hardware id. */
if ((id & HX280_ID_PRODUCT_MSK) != HX280_PRODUCT_ID_R1 &&
(id & HX280_ID_PRODUCT_MSK) != HX280_PRODUCT_ID_R2) {
dev_err(hxdev->device.parent,
"failed to find valid hardware id (0x%08x).\n",
id);
return -ENODEV;
}
dev_info(hxdev->device.parent,
"found hx280 video encoder (0x%08x).\n",
id);
/* Stop processing. */
writel_relaxed(0, regs + HX280_COMCTRL);
/* Disable & acknowledge interrupts. */
writel(HX280_IRQ_INTDISABLE, regs + HX280_IRQ);
/* Clear all remaining registers. FIXME: is this really necessary ? */
for (r = 2 * sizeof(u32);
r < (sizeof(u32) * HX280_REGS_NR);
r += sizeof(u32))
writel_relaxed(0, regs + r);
wmb();
return 0;
}
static int __devinit hx280_probe(struct platform_device* pdev)
{
int err;
struct hx_unit* const unit = kmalloc(sizeof(*unit), GFP_KERNEL);
if (! unit)
return -ENOMEM;
hx_init_unit(unit, &hx280_handle_irq, &hx280_setup_irq);
err = hx_probe(pdev, "hx280", unit, 1, &hx280_init_hw);
if (err)
kfree(unit);
return err;
}
static int __devexit hx280_remove(struct platform_device* pdev)
{
int err;
struct hx_unit* const unit = ((struct hx_device*)
dev_get_platdata(&pdev->dev))->units;
err = hx_remove(pdev);
if (! err)
kfree(unit);
return err;
}
static int hx280_suspend(struct platform_device *pdev, pm_message_t state)
{
return hx_suspend(pdev);
}
static int hx280_resume(struct platform_device *pdev)
{
return hx_resume(pdev, &hx280_init_hw);
}
static struct platform_driver hx280_driver = {
.probe = hx280_probe,
.remove = __devexit_p(hx280_remove),
.driver = {
.name = HX280_DRV_NAME,
.owner = THIS_MODULE,
},
.suspend = &hx280_suspend,
.resume = &hx280_resume
};
module_platform_driver(hx280_driver);
MODULE_AUTHOR("Gregor Boirie <gregor.boirie@parrot.com>");
MODULE_DESCRIPTION("On2 / Hantro video encoder "
"userspace I/O platform driver");
MODULE_VERSION(DRV_VERSION);
MODULE_LICENSE("GPL");