From patchwork Fri Feb 1 09:20:38 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhou Zhu X-Patchwork-Id: 2077911 Return-Path: X-Original-To: patchwork-linux-fbdev@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork2.kernel.org (Postfix) with ESMTP id 35C77DFE75 for ; Fri, 1 Feb 2013 09:21:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756041Ab3BAJVX (ORCPT ); Fri, 1 Feb 2013 04:21:23 -0500 Received: from na3sys009aog113.obsmtp.com ([74.125.149.209]:40108 "EHLO na3sys009aog113.obsmtp.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755700Ab3BAJVW (ORCPT ); Fri, 1 Feb 2013 04:21:22 -0500 Received: from MSI-MTA.marvell.com ([65.219.4.132]) (using TLSv1) by na3sys009aob113.postini.com ([74.125.148.12]) with SMTP ID DSNKUQuJDmqHrJd8tOIcLCl6SD6cwvAWvFdL@postini.com; Fri, 01 Feb 2013 01:21:22 PST Received: from maili.marvell.com ([10.68.76.210]) by MSI-MTA.marvell.com with Microsoft SMTPSVC(6.0.3790.4675); Fri, 1 Feb 2013 01:20:48 -0800 Received: from localhost (unknown [10.38.36.52]) by maili.marvell.com (Postfix) with ESMTP id 217BD4E517; Fri, 1 Feb 2013 01:20:48 -0800 (PST) From: Zhou Zhu To: , , Andrew Morton , FlorianSchandinat@gmx.de Cc: , Greg Kroah-Hartman , Lisa Du , Guoqing Li , Zhou Zhu Subject: [PATCH v6 5/8] video: mmpdisp: add spi port in display controller Date: Fri, 1 Feb 2013 17:20:38 +0800 Message-Id: <1359710438-9850-1-git-send-email-zzhu3@marvell.com> X-Mailer: git-send-email 1.7.9.5 X-OriginalArrivalTime: 01 Feb 2013 09:20:48.0510 (UTC) FILETIME=[6C0D69E0:01CE005D] Sender: linux-fbdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org Added spi port support in mmp display controller. This port is from display controller and for panel usage. This driver implemented and registered as a spi master. Signed-off-by: Zhou Zhu Acked-by: Haojian Zhuang --- drivers/video/mmp/hw/Kconfig | 8 ++ drivers/video/mmp/hw/Makefile | 1 + drivers/video/mmp/hw/mmp_ctrl.c | 6 ++ drivers/video/mmp/hw/mmp_ctrl.h | 4 + drivers/video/mmp/hw/mmp_spi.c | 180 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 199 insertions(+) create mode 100644 drivers/video/mmp/hw/mmp_spi.c diff --git a/drivers/video/mmp/hw/Kconfig b/drivers/video/mmp/hw/Kconfig index 6c1dd34..02f109a 100644 --- a/drivers/video/mmp/hw/Kconfig +++ b/drivers/video/mmp/hw/Kconfig @@ -9,4 +9,12 @@ config MMP_DISP_CONTROLLER this controller is used on Marvell PXA910, MMP2, MMP3, PXA988 chips +config MMP_DISP_SPI + bool "mmp display controller spi port" + depends on MMP_DISP_CONTROLLER && SPI_MASTER + default y + help + Marvell MMP display hw controller spi port support + will register as a spi master for panel usage + endif diff --git a/drivers/video/mmp/hw/Makefile b/drivers/video/mmp/hw/Makefile index f34ace8..0000a71 100644 --- a/drivers/video/mmp/hw/Makefile +++ b/drivers/video/mmp/hw/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_MMP_DISP_CONTROLLER) += mmp_ctrl.o +obj-$(CONFIG_MMP_DISP_SPI) += mmp_spi.o diff --git a/drivers/video/mmp/hw/mmp_ctrl.c b/drivers/video/mmp/hw/mmp_ctrl.c index 1b5d299..4bd31b2 100644 --- a/drivers/video/mmp/hw/mmp_ctrl.c +++ b/drivers/video/mmp/hw/mmp_ctrl.c @@ -535,6 +535,12 @@ static int mmphw_probe(struct platform_device *pdev) } } +#ifdef CONFIG_MMP_DISP_SPI + ret = lcd_spi_register(ctrl); + if (ret < 0) + goto failed_path_init; +#endif + dev_info(ctrl->dev, "device init done\n"); return 0; diff --git a/drivers/video/mmp/hw/mmp_ctrl.h b/drivers/video/mmp/hw/mmp_ctrl.h index b125f53..6408d8e 100644 --- a/drivers/video/mmp/hw/mmp_ctrl.h +++ b/drivers/video/mmp/hw/mmp_ctrl.h @@ -1967,4 +1967,8 @@ static inline struct lcd_regs *path_regs(struct mmp_path *path) return NULL; } } + +#ifdef CONFIG_MMP_DISP_SPI +extern int lcd_spi_register(struct mmphw_ctrl *ctrl); +#endif #endif /* _MMP_CTRL_H_ */ diff --git a/drivers/video/mmp/hw/mmp_spi.c b/drivers/video/mmp/hw/mmp_spi.c new file mode 100644 index 0000000..e62ca7b --- /dev/null +++ b/drivers/video/mmp/hw/mmp_spi.c @@ -0,0 +1,180 @@ +/* + * linux/drivers/video/mmp/hw/mmp_spi.c + * using the spi in LCD controler for commands send + * + * Copyright (C) 2012 Marvell Technology Group Ltd. + * Authors: Guoqing Li + * Lisa Du + * Zhou Zhu + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + */ +#include +#include +#include +#include +#include +#include "mmp_ctrl.h" + +/** + * spi_write - write command to the SPI port + * @data: can be 8/16/32-bit, MSB justified data to write. + * @len: data length. + * + * Wait bus transfer complete IRQ. + * The caller is expected to perform the necessary locking. + * + * Returns: + * %-ETIMEDOUT timeout occurred + * 0 success + */ +static inline int lcd_spi_write(struct spi_device *spi, u32 data) +{ + int timeout = 100000, isr, ret = 0; + u32 tmp; + void *reg_base = + *(void **)spi_master_get_devdata(spi->master); + + /* clear ISR */ + writel_relaxed(~SPI_IRQ_MASK, reg_base + SPU_IRQ_ISR); + + switch (spi->bits_per_word) { + case 8: + writel_relaxed((u8)data, reg_base + LCD_SPU_SPI_TXDATA); + break; + case 16: + writel_relaxed((u16)data, reg_base + LCD_SPU_SPI_TXDATA); + break; + case 32: + writel_relaxed((u32)data, reg_base + LCD_SPU_SPI_TXDATA); + break; + default: + dev_err(&spi->dev, "Wrong spi bit length\n"); + } + + /* SPI start to send command */ + tmp = readl_relaxed(reg_base + LCD_SPU_SPI_CTRL); + tmp &= ~CFG_SPI_START_MASK; + tmp |= CFG_SPI_START(1); + writel(tmp, reg_base + LCD_SPU_SPI_CTRL); + + isr = readl_relaxed(reg_base + SPU_IRQ_ISR); + while (!(isr & SPI_IRQ_ENA_MASK)) { + udelay(100); + isr = readl_relaxed(reg_base + SPU_IRQ_ISR); + if (!--timeout) { + ret = -ETIMEDOUT; + dev_err(&spi->dev, "spi cmd send time out\n"); + break; + } + } + + tmp = readl_relaxed(reg_base + LCD_SPU_SPI_CTRL); + tmp &= ~CFG_SPI_START_MASK; + tmp |= CFG_SPI_START(0); + writel_relaxed(tmp, reg_base + LCD_SPU_SPI_CTRL); + + writel_relaxed(~SPI_IRQ_MASK, reg_base + SPU_IRQ_ISR); + + return ret; +} + +static int lcd_spi_setup(struct spi_device *spi) +{ + void *reg_base = + *(void **)spi_master_get_devdata(spi->master); + u32 tmp; + + tmp = CFG_SCLKCNT(16) | + CFG_TXBITS(spi->bits_per_word) | + CFG_SPI_SEL(1) | CFG_SPI_ENA(1) | + CFG_SPI_3W4WB(1); + writel(tmp, reg_base + LCD_SPU_SPI_CTRL); + + /* + * After set mode it need a time to pull up the spi singals, + * or it would cause the wrong waveform when send spi command, + * especially on pxa910h + */ + tmp = readl_relaxed(reg_base + SPU_IOPAD_CONTROL); + if ((tmp & CFG_IOPADMODE_MASK) != IOPAD_DUMB18SPI) + writel_relaxed(IOPAD_DUMB18SPI | + (tmp & ~CFG_IOPADMODE_MASK), + reg_base + SPU_IOPAD_CONTROL); + udelay(20); + return 0; +} + +static int lcd_spi_one_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct spi_transfer *t; + int i; + + list_for_each_entry(t, &m->transfers, transfer_list) { + switch (spi->bits_per_word) { + case 8: + for (i = 0; i < t->len; i++) + lcd_spi_write(spi, ((u8 *)t->tx_buf)[i]); + break; + case 16: + for (i = 0; i < t->len/2; i++) + lcd_spi_write(spi, ((u16 *)t->tx_buf)[i]); + break; + case 32: + for (i = 0; i < t->len/4; i++) + lcd_spi_write(spi, ((u32 *)t->tx_buf)[i]); + break; + default: + dev_err(&spi->dev, "Wrong spi bit length\n"); + } + } + + m->status = 0; + if (m->complete) + m->complete(m->context); + return 0; +} + +int lcd_spi_register(struct mmphw_ctrl *ctrl) +{ + struct spi_master *master; + void **p_regbase; + int err; + + master = spi_alloc_master(ctrl->dev, sizeof(void *)); + if (!master) { + dev_err(ctrl->dev, "unable to allocate SPI master\n"); + return -ENOMEM; + } + p_regbase = spi_master_get_devdata(master); + *p_regbase = ctrl->reg_base; + + /* set bus num to 5 to avoid conflict with other spi hosts */ + master->bus_num = 5; + master->num_chipselect = 1; + master->setup = lcd_spi_setup; + master->transfer = lcd_spi_one_transfer; + + err = spi_register_master(master); + if (err < 0) { + dev_err(ctrl->dev, "unable to register SPI master\n"); + spi_master_put(master); + return err; + } + + dev_info(&master->dev, "registered\n"); + + return 0; +}