工具与软件:
您好、ti 专家:
我们使用 SK-AM62A-LP 平台和 SDK 09.02。
我们使用 GPMC 接口连接到 OLED (I-80模式) 模块。 并使用 DMA 将数据从帧缓冲区复制到 OLED 格式。
DMA 可以工作、OLED 可以显示照片、但照片颜色会反转、红色更改为蓝色。 原因是:OLED 的格式是大端,而 DMA 在小端字节序上是 wokes, DMA 的地址从低地址增加到高地址。 那么、如何解决该问题呢?
我参考了补丁文件 338.udma-dev-to-mem-1019.diff、并 为 drivers\dma\ti\k3-udma.c 打补丁
AM62A3:GPMC 器件寄存器至存储器传输的 Linux DMA 标识符-处理器论坛-处理器- TI E2E 支持论坛
DTS 和代码如下:
&gpmc0 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&gpmc0_pins_default>;
assigned-clock-parents = <&k3_clks 80 2>; /*1mclk=10ns*/
assigned-clock-rates = <33333333>; /*33333333 ,30 ns*/
ranges = <1 0 0x00 0x50000000 0x01000000>; /* CS1 space. size = 128MiB*/
oled@1,0 {
compatible = "atmel,unication-oled-module";
reg = <1 0 0x00000004>;
reg-names = "data";
oled_rs = <22>; /* pin index for LCD_RS */
madctl = <0x00>; //bit3:0-rgb, 1-bgr. bit6:1-mirror-x
oled_type = <0x00>;//1: rm96092, 0: FT2308
rotate_degree = <270>; //clockwide, vaild for 0,90,180,270
atmel,oled-has-dma;
atmel,oled-bank-width = <8>; /* 8bit */
dmas = <&main_bcdma 1 0 0>;
dma-names = "tx";
oled_reset-gpios = <&main_gpio0 38 GPIO_ACTIVE_HIGH>;
oled_panel_pw_ctl-gpios = <&main_gpio0 14 GPIO_ACTIVE_HIGH>;
interrupt-parent = <&main_gpio0>;
interrupts = <36 IRQ_TYPE_EDGE_FALLING>; //Tearing Effect(TE) singal
//assigned-clock-parents = <&k3_clks 80 2>; /*1mclk=10ns*/
//assigned-clock-rates = <33333333>; /*33333333 ,30 ns*/
// can read 3208 ID ,can display R G B
// mclk=30 ns
/* CONFIG1*/
bank-width = <1>;
/*gpmc,sync-read;*/
/*gpmc,sync-write;*/
/*gpmc,clk-activation-ns = <0>;*/
gpmc,mux-add-data = <0>;
/* CONFIG2*/
/*gpmc,sync-clk-ps = <0>;*/
gpmc,cs-on-ns = <30>;
gpmc,cs-rd-off-ns = <900>;
gpmc,cs-wr-off-ns = <228>;
/* CONFIG3*/
/*gpmc,adv-on-ns = <35>;*/
/*gpmc,adv-rd-off-ns = <9>;*/
/*gpmc,adv-wr-off-ns = <9>;*/
/* CONFIG4*/
gpmc,we-on-ns = <53>;
gpmc,we-off-ns = <129>;
gpmc,oe-on-ns = <53>;
gpmc,oe-off-ns = <454>;
/* CONFIG5*/
gpmc,access-ns = <454>;
gpmc,rd-cycle-ns = <930>;
gpmc,wr-cycle-ns = <227>;
gpmc,wr-access-ns = <129>;
};
};
static inline int set_dma_config(struct oledfb_dev *oled_dev)
{
int ret;
struct dma_slave_config slave_config;
oled_dev->dma_chan = dma_request_slave_channel(oled_dev->dev, "tx");
if(!oled_dev->dma_chan) {
dev_err(oled_dev->dev, "%s Failed to request DMA channel\n", __FUNCTION__);
return -EINVAL;
}
dev_info(oled_dev->dev, "oled using %s for DMA transfer\n", dma_chan_name(oled_dev->dma_chan));
memset(&slave_config, 0, sizeof(slave_config));
slave_config.direction = DMA_MEM_TO_DEV;
slave_config.dst_addr_width = 1;
slave_config.dst_maxburst = 1;
slave_config.dst_addr = (dma_addr_t)oled_dev->oled_data_phy_addr;
ret = dmaengine_slave_config(oled_dev->dma_chan, &slave_config);
if(ret) {
dev_err(oled_dev->dev, "error in dma configuration\n");
goto err;
}
return 0;
err:
dma_release_channel(oled_dev->dma_chan);
oled_dev->dma_chan = NULL;
return ret;
}
static inline int prepare_dma_transfer(struct oledfb_dev *oled_dev)
{
struct scatterlist *sgl = &oled_dev->sgl[0];
struct dma_async_tx_descriptor *desc;
struct dma_chan *chan = oled_dev->dma_chan;
dma_addr_t p;
if (!oled_dev->dma_chan) {
if (set_dma_config(oled_dev) < 0) {
dev_err(oled_dev->dev, "%s: set_dma_config fail\n", __FUNCTION__);
return -1;
}
}
/*
The maximun transaction units is 0xFFFF length.
We have logo data 448*368*2= 329728bytes to be send, need at least 6 scatters
transactions. 0xFFFF(65535)*4 + 33794*2 =329728bytes.
*/
/* Since ARM's memory is little-endian, while the driver IC needs to be feed
* in big-endian, hence the walk around is setup DMA transfering
* from buffer memory's tail to head, with address decreasing */
sg_init_table(sgl, 6);
p = oled_dev->rotated_framebuf_phy_start;
sg_dma_address(&sgl[0]) = p;
sg_dma_len(&sgl[0]) = 0xFFFF;
p = p + 0xFFFF;
sg_dma_address(&sgl[1]) = p;
sg_dma_len(&sgl[1]) = 0xFFFF;
p = p + 0xFFFF;
sg_dma_address(&sgl[2]) = p;
sg_dma_len(&sgl[2]) = 0xFFFF;
p = p + 0xFFFF;
sg_dma_address(&sgl[3]) = p;
sg_dma_len(&sgl[3]) = 0xFFFF;
p = p + 0xFFFF;
sg_dma_address(&sgl[4]) = p;
sg_dma_len(&sgl[4]) = 33794;
p = p + 33794;
sg_dma_address(&sgl[5]) = p;
sg_dma_len(&sgl[5]) = 33794;
desc = dmaengine_prep_slave_sg(chan, sgl, 6,
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT|DMA_CTRL_ACK);
if(desc == NULL) {
dev_err(oled_dev->dev, "get dma desc fail.\n");
return -1;
}
oled_dev->desc_tx = desc;
desc->callback = dma_complete_callback_function;
desc->callback_param = oled_dev;
dma_cookie_t cookie_tx;
/* Start DMA transmition, send RGB data */
cookie_tx = dmaengine_submit(oled_dev->desc_tx);
txchan->device->device_issue_pending(txchan);
return 0;
}
static int update_oled_contents_by_dma(struct oledfb_dev *oled_dev)
{
int ret;
dev_info(oled_dev->dev, "update_oled_contents_by_dma ENTER.\n");
if(oled_dev->desc_tx) {
dev_info(oled_dev->dev, "The old screen update request has not been executed yet!\n");
return 0;
}
ret = prepare_dma_transfer(oled_dev);
if (ret < 0) {
dev_err(oled_dev->dev, "%s: prepare_dma_transfer fail: %d\n", __FUNCTION__, ret);
return ret;
}
if(oled_dev->oled_id == OLED_ID_FT2308)
{
/* Tearing Effect Line OFF */
oled_ft2308_cmd(oled_dev, 0x34);
/* Memory Write */
oled_ft2308_cmd(oled_dev, 0x2C);
}
else
{
/* Tearing Effect Line OFF */
oled_rm69092_cmd(oled_dev, 0x3400);
/* Memory Write */
oled_rm69092_cmd(oled_dev, 0x2C00);
}
struct dma_chan *txchan = oled_dev->dma_chan;
dma_cookie_t cookie_tx;
/* Start DMA transmition, send RGB data */
cookie_tx = dmaengine_submit(oled_dev->desc_tx);
txchan->device->device_issue_pending(txchan);
dev_info(oled_dev->dev, "update_oled_contents_by_dma END. cookie_tx=%d\n", cookie_tx);
return 0;
}
/* Issue pending requests and wait for callback notification */
static void dma_complete_callback_function(void *dma_async_param)
{
struct oledfb_dev *oled_dev = (struct oledfb_dev *)dma_async_param;
struct dma_chan *txchan = oled_dev->dma_chan;
async_tx_ack(oled_dev->desc_tx);
oled_dev->desc_tx = NULL;
oled_dev->refresh_complete = 1;
}











