video: mb862xx: add support for controller's I2C bus adapter
Add adapter driver for I2C adapter in Coral-P(A)/Lime GDCs. So we can easily access devices on controller's I2C bus using i2c-dev interface. Signed-off-by: Anatolij Gustschin <agust@denx.de>
This commit is contained in:
parent
12ed0c4baa
commit
f8a6b1f448
|
@ -2294,6 +2294,15 @@ config FB_MB862XX_LIME
|
|||
|
||||
endchoice
|
||||
|
||||
config FB_MB862XX_I2C
|
||||
bool "Support I2C bus on MB862XX GDC"
|
||||
depends on FB_MB862XX && I2C
|
||||
default y
|
||||
help
|
||||
Selecting this option adds Coral-P(A)/Lime GDC I2C bus adapter
|
||||
driver to support accessing I2C devices on controller's I2C bus.
|
||||
These are usually some video decoder chips.
|
||||
|
||||
config FB_EP93XX
|
||||
tristate "EP93XX frame buffer support"
|
||||
depends on FB && ARCH_EP93XX
|
||||
|
|
|
@ -2,4 +2,7 @@
|
|||
# Makefile for the MB862xx framebuffer driver
|
||||
#
|
||||
|
||||
obj-$(CONFIG_FB_MB862XX) := mb862xxfb.o mb862xxfb_accel.o
|
||||
obj-$(CONFIG_FB_MB862XX) += mb862xxfb.o
|
||||
|
||||
mb862xxfb-y := mb862xxfbdrv.o mb862xxfb_accel.o
|
||||
mb862xxfb-$(CONFIG_FB_MB862XX_I2C) += mb862xx-i2c.o
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* Coral-P(A)/Lime I2C adapter driver
|
||||
*
|
||||
* (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/fb.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "mb862xxfb.h"
|
||||
#include "mb862xx_reg.h"
|
||||
|
||||
static int mb862xx_i2c_wait_event(struct i2c_adapter *adap)
|
||||
{
|
||||
struct mb862xxfb_par *par = adap->algo_data;
|
||||
u32 reg;
|
||||
|
||||
do {
|
||||
udelay(1);
|
||||
reg = inreg(i2c, GC_I2C_BCR);
|
||||
if (reg & (I2C_INT | I2C_BER))
|
||||
break;
|
||||
} while (1);
|
||||
|
||||
return (reg & I2C_BER) ? 0 : 1;
|
||||
}
|
||||
|
||||
static int mb862xx_i2c_do_address(struct i2c_adapter *adap, int addr)
|
||||
{
|
||||
struct mb862xxfb_par *par = adap->algo_data;
|
||||
|
||||
outreg(i2c, GC_I2C_DAR, addr);
|
||||
outreg(i2c, GC_I2C_CCR, I2C_CLOCK_AND_ENABLE);
|
||||
outreg(i2c, GC_I2C_BCR, par->i2c_rs ? I2C_REPEATED_START : I2C_START);
|
||||
if (!mb862xx_i2c_wait_event(adap))
|
||||
return -EIO;
|
||||
par->i2c_rs = !(inreg(i2c, GC_I2C_BSR) & I2C_LRB);
|
||||
return par->i2c_rs;
|
||||
}
|
||||
|
||||
static int mb862xx_i2c_write_byte(struct i2c_adapter *adap, u8 byte)
|
||||
{
|
||||
struct mb862xxfb_par *par = adap->algo_data;
|
||||
|
||||
outreg(i2c, GC_I2C_DAR, byte);
|
||||
outreg(i2c, GC_I2C_BCR, I2C_START);
|
||||
if (!mb862xx_i2c_wait_event(adap))
|
||||
return -EIO;
|
||||
return !(inreg(i2c, GC_I2C_BSR) & I2C_LRB);
|
||||
}
|
||||
|
||||
static int mb862xx_i2c_read_byte(struct i2c_adapter *adap, u8 *byte, int last)
|
||||
{
|
||||
struct mb862xxfb_par *par = adap->algo_data;
|
||||
|
||||
outreg(i2c, GC_I2C_BCR, I2C_START | (last ? 0 : I2C_ACK));
|
||||
if (!mb862xx_i2c_wait_event(adap))
|
||||
return 0;
|
||||
*byte = inreg(i2c, GC_I2C_DAR);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void mb862xx_i2c_stop(struct i2c_adapter *adap)
|
||||
{
|
||||
struct mb862xxfb_par *par = adap->algo_data;
|
||||
|
||||
outreg(i2c, GC_I2C_BCR, I2C_STOP);
|
||||
outreg(i2c, GC_I2C_CCR, I2C_DISABLE);
|
||||
par->i2c_rs = 0;
|
||||
}
|
||||
|
||||
static int mb862xx_i2c_read(struct i2c_adapter *adap, struct i2c_msg *m)
|
||||
{
|
||||
int i, ret = 0;
|
||||
int last = m->len - 1;
|
||||
|
||||
for (i = 0; i < m->len; i++) {
|
||||
if (!mb862xx_i2c_read_byte(adap, &m->buf[i], i == last)) {
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mb862xx_i2c_write(struct i2c_adapter *adap, struct i2c_msg *m)
|
||||
{
|
||||
int i, ret = 0;
|
||||
|
||||
for (i = 0; i < m->len; i++) {
|
||||
if (!mb862xx_i2c_write_byte(adap, m->buf[i])) {
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mb862xx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
struct mb862xxfb_par *par = adap->algo_data;
|
||||
struct i2c_msg *m;
|
||||
int addr;
|
||||
int i = 0, err = 0;
|
||||
|
||||
dev_dbg(par->dev, "%s: %d msgs\n", __func__, num);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
m = &msgs[i];
|
||||
if (!m->len) {
|
||||
dev_dbg(par->dev, "%s: null msgs\n", __func__);
|
||||
continue;
|
||||
}
|
||||
addr = m->addr;
|
||||
if (m->flags & I2C_M_RD)
|
||||
addr |= 1;
|
||||
|
||||
err = mb862xx_i2c_do_address(adap, addr);
|
||||
if (err < 0)
|
||||
break;
|
||||
if (m->flags & I2C_M_RD)
|
||||
err = mb862xx_i2c_read(adap, m);
|
||||
else
|
||||
err = mb862xx_i2c_write(adap, m);
|
||||
}
|
||||
|
||||
if (i)
|
||||
mb862xx_i2c_stop(adap);
|
||||
|
||||
return (err < 0) ? err : i;
|
||||
}
|
||||
|
||||
static u32 mb862xx_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_SMBUS_BYTE_DATA;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm mb862xx_algo = {
|
||||
.master_xfer = mb862xx_xfer,
|
||||
.functionality = mb862xx_func,
|
||||
};
|
||||
|
||||
static struct i2c_adapter mb862xx_i2c_adapter = {
|
||||
.name = "MB862xx I2C adapter",
|
||||
.algo = &mb862xx_algo,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
int mb862xx_i2c_init(struct mb862xxfb_par *par)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mb862xx_i2c_adapter.algo_data = par;
|
||||
par->adap = &mb862xx_i2c_adapter;
|
||||
|
||||
ret = i2c_add_adapter(par->adap);
|
||||
if (ret < 0) {
|
||||
dev_err(par->dev, "failed to add %s\n",
|
||||
mb862xx_i2c_adapter.name);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void mb862xx_i2c_exit(struct mb862xxfb_par *par)
|
||||
{
|
||||
if (par->adap) {
|
||||
i2c_del_adapter(par->adap);
|
||||
par->adap = NULL;
|
||||
}
|
||||
}
|
|
@ -80,6 +80,27 @@
|
|||
|
||||
#define GC_DISP_REFCLK_400 400
|
||||
|
||||
/* I2C */
|
||||
#define GC_I2C_BSR 0x00000000 /* BSR */
|
||||
#define GC_I2C_BCR 0x00000004 /* BCR */
|
||||
#define GC_I2C_CCR 0x00000008 /* CCR */
|
||||
#define GC_I2C_ADR 0x0000000C /* ADR */
|
||||
#define GC_I2C_DAR 0x00000010 /* DAR */
|
||||
|
||||
#define I2C_DISABLE 0x00000000
|
||||
#define I2C_STOP 0x00000000
|
||||
#define I2C_START 0x00000010
|
||||
#define I2C_REPEATED_START 0x00000030
|
||||
#define I2C_CLOCK_AND_ENABLE 0x0000003f
|
||||
#define I2C_READY 0x01
|
||||
#define I2C_INT 0x01
|
||||
#define I2C_INTE 0x02
|
||||
#define I2C_ACK 0x08
|
||||
#define I2C_BER 0x80
|
||||
#define I2C_BEIE 0x40
|
||||
#define I2C_TRX 0x80
|
||||
#define I2C_LRB 0x10
|
||||
|
||||
/* Carmine specific */
|
||||
#define MB86297_DRAW_BASE 0x00020000
|
||||
#define MB86297_DISP0_BASE 0x00100000
|
||||
|
|
|
@ -57,11 +57,20 @@ struct mb862xxfb_par {
|
|||
unsigned int refclk; /* disp. reference clock */
|
||||
struct mb862xx_gc_mode *gc_mode; /* GDC mode init data */
|
||||
int pre_init; /* don't init display if 1 */
|
||||
struct i2c_adapter *adap; /* GDC I2C bus adapter */
|
||||
int i2c_rs;
|
||||
|
||||
u32 pseudo_palette[16];
|
||||
};
|
||||
|
||||
extern void mb862xxfb_init_accel(struct fb_info *info, int xres);
|
||||
#ifdef CONFIG_FB_MB862XX_I2C
|
||||
extern int mb862xx_i2c_init(struct mb862xxfb_par *par);
|
||||
extern void mb862xx_i2c_exit(struct mb862xxfb_par *par);
|
||||
#else
|
||||
static inline int mb862xx_i2c_init(struct mb862xxfb_par *par) { return 0; }
|
||||
static inline void mb862xx_i2c_exit(struct mb862xxfb_par *par) { }
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_FB_MB862XX_LIME) && defined(CONFIG_FB_MB862XX_PCI_GDC)
|
||||
#error "Select Lime GDC or CoralP/Carmine support, but not both together"
|
||||
|
|
|
@ -772,6 +772,8 @@ static int coralp_init(struct mb862xxfb_par *par)
|
|||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mb862xx_i2c_init(par);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1029,6 +1031,8 @@ static void __devexit mb862xx_pci_remove(struct pci_dev *pdev)
|
|||
outreg(host, GC_IMASK, 0);
|
||||
}
|
||||
|
||||
mb862xx_i2c_exit(par);
|
||||
|
||||
device_remove_file(&pdev->dev, &dev_attr_dispregs);
|
||||
|
||||
pci_set_drvdata(pdev, NULL);
|
Loading…
Reference in New Issue