Re: [RFC/PATCH 4/6] FBDEV: JZ4740: Add Smart LCD controller support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 03/01/2011 06:25 PM, Maurus Cuelenaere wrote:
>> On 03/01/2011 01:06 PM, Maurus Cuelenaere wrote:
>>> This patch adds SLCD support to the Ingenic Jz4740 framebuffer driver.
>>> Besides adding the new LCD types to the platform data, this adds chip select
>>> and register select active low support (SLCD only). Also, this exports some
>>> functions related to starting and stopping the SLCD image transfer and sending
>>> commands.
>>>
>>> A lot of this code was based on work by Maarten ter Huurne:
>>> https://github.com/mthuurne/opendingux-kernel/commit/3dba5b5e7a868b1067acae8d1b5a19121b77bc6a
>>>
>>> Signed-off-by: Maurus Cuelenaere <mcuelenaere@xxxxxxxxx>
>> Looks mostly good :)
>>
>>> ---
>>>  arch/mips/include/asm/mach-jz4740/jz4740_fb.h |   33 ++
>>>  drivers/video/jz4740_fb.c                     |  435 ++++++++++++++++++++++---
>>>  2 files changed, 429 insertions(+), 39 deletions(-)
>>>
>>> diff --git a/arch/mips/include/asm/mach-jz4740/jz4740_fb.h b/arch/mips/include/asm/mach-jz4740/jz4740_fb.h
>>> index 6a50e6f..eaaac43 100644
>>> --- a/arch/mips/include/asm/mach-jz4740/jz4740_fb.h
>>> +++ b/arch/mips/include/asm/mach-jz4740/jz4740_fb.h
>>> @@ -30,6 +30,13 @@ enum jz4740_fb_lcd_type {
>>>  	JZ_LCD_TYPE_DUAL_COLOR_STN = 10,
>>>  	JZ_LCD_TYPE_DUAL_MONOCHROME_STN = 11,
>>>  	JZ_LCD_TYPE_8BIT_SERIAL = 12,
>>> +
>>> +	JZ_SLCD_TYPE_PARALLEL_8_BIT = 1 | (1 << 5),
>>> +	JZ_SLCD_TYPE_PARALLEL_16_BIT = 0 | (1 << 5),
>>> +	JZ_SLCD_TYPE_PARALLEL_18_BIT = 2 | (1 << 5),
>>> +	JZ_SLCD_TYPE_SERIAL_8_BIT = 1 | (3 << 5),
>>> +	JZ_SLCD_TYPE_SERIAL_16_BIT = 0 | (3 << 5),
>>> +	JZ_SLCD_TYPE_SERIAL_18_BIT = 2 | (3 << 5),
>> I would prefer
>> JZ_LCD_TYPE_SLCD_...
> 
> Ok.
> 
>>>  };
>> Maybe add
>> #define JZ_LCD_TYPE_SLCD (1 << 5)
>> #define JZ_LCD_TYPE_SLCD_SERIAL (JZ_LCD_TYPE_SLCD | (1 << 6))
> 
> Ok.
> 
>>
>>>  
>>>  #define JZ4740_FB_SPECIAL_TFT_CONFIG(start, stop) (((start) << 16) | (stop))
>>> @@ -62,6 +69,32 @@ struct jz4740_fb_platform_data {
>>>  
>>>  	unsigned pixclk_falling_edge:1;
>>>  	unsigned date_enable_active_low:1;
>>> +	unsigned chip_select_active_low:1;
>>> +	unsigned register_select_active_low:1;
>>>  };
>>>  
>>> +struct platform_device;
>>> +
>>> +/*
>>> + * JzFB SLCD related functions
>>> + *
>>> + * jz4740_fb_slcd_disable_transfer: disables the current image transfer going
>>> + *       on between memory and the LCD controller
>>> + * jz4740_fb_slcd_enable_transfer: the reverse operation of the above
>>> + * jz4740_fb_slcd_send_cmd: sends a command without any data to the LCD
>>> + *       controller
>>> + * jz4740_fb_slcd_send_cmd_data: sends a command with a data argument to the LCD
>>> + *       controller
>>> + *
>>> + * These functions can sleep and thus should not be called from an atomic
>>> + * context. Also, make sure you disable the SLCD image transfer *before* sending
>>> + * any commands and do not forget to re-enable it.
>>> + */
>>> +extern void jz4740_fb_slcd_disable_transfer(struct platform_device *pdev);
>>> +extern void jz4740_fb_slcd_enable_transfer(struct platform_device *pdev);
>>> +extern void jz4740_fb_slcd_send_cmd_data(struct platform_device *pdev,
>>> +					 unsigned int cmd, unsigned int data);
>>> +extern void jz4740_fb_slcd_send_cmd(struct platform_device *pdev,
>>> +				    unsigned int cmd);
>>> +
>>>  #endif
>>> diff --git a/drivers/video/jz4740_fb.c b/drivers/video/jz4740_fb.c
>>> index 2f3ea57..4064812 100644
>>> --- a/drivers/video/jz4740_fb.c
>>> +++ b/drivers/video/jz4740_fb.c
>>> @@ -26,6 +26,7 @@
>>>  
>>>  #include <linux/dma-mapping.h>
>>>  
>>> +#include <asm/mach-jz4740/dma.h>
>>>  #include <asm/mach-jz4740/jz4740_fb.h>
>>>  #include <asm/mach-jz4740/gpio.h>
>>>  
>>> @@ -107,6 +108,40 @@
>>>  
>>>  #define JZ_LCD_STATE_DISABLED BIT(0)
>>>  
>>> +#define JZ_REG_SLCD_CFG		0xA0
>>> +#define JZ_REG_SLCD_CTRL	0xA4
>>> +#define JZ_REG_SLCD_STATE	0xA8
>>> +#define JZ_REG_SLCD_DATA	0xAC
>>> +#define JZ_REG_SLCD_FIFO	0xB0
>>> +
>>> +#define JZ_SLCD_CFG_BURST_8_WORD	BIT(14)
>>> +#define JZ_SLCD_CFG_DWIDTH_MASK		(7 << 10)
>>> +#define JZ_SLCD_CFG_DWIDTH_18		(0 << 10)
>>> +#define JZ_SLCD_CFG_DWIDTH_16		(1 << 10)
>>> +#define JZ_SLCD_CFG_DWIDTH_8_x3		(2 << 10)
>>> +#define JZ_SLCD_CFG_DWIDTH_8_x2		(3 << 10)
>>> +#define JZ_SLCD_CFG_DWIDTH_8_x1		(4 << 10)
>>> +#define JZ_SLCD_CFG_DWIDTH_9_x2		(7 << 10)
>>> +#define JZ_SLCD_CFG_CWIDTH_MASK		(3 << 8)
>>> +#define JZ_SLCD_CFG_CWIDTH(n)		((n) << 8)
>>> +#define JZ_SLCD_CFG_CWIDTH_16BIT	(0 << 8)
>>> +#define JZ_SLCD_CFG_CWIDTH_8BIT		(1 << 8)
>>> +#define JZ_SLCD_CFG_CWIDTH_18BIT	(2 << 8)
>>> +#define JZ_SLCD_CFG_CS_ACTIVE_HIGH	BIT(4)
>>> +#define JZ_SLCD_CFG_RS_CMD_HIGH		BIT(3)
>>> +#define JZ_SLCD_CFG_CLK_ACTIVE_RISING	BIT(1)
>>> +#define JZ_SLCD_CFG_TYPE_SERIAL		BIT(0)
>>> +
>>> +#define JZ_SLCD_CTRL_DMA_EN		(1 << 0)
>>> +
>>> +#define JZ_SLCD_STATE_BUSY		(1 << 0)
>>> +
>>> +#define JZ_SLCD_DATA_RS_DATA		(0 << 31)
>>> +#define JZ_SLCD_DATA_RS_COMMAND		(1 << 31)
>>> +
>>> +#define JZ_SLCD_FIFO_RS_DATA		(0 << 31)
>>> +#define JZ_SLCD_FIFO_RS_COMMAND		(1 << 31)
>>> +
>>>  struct jzfb_framedesc {
>>>  	uint32_t next;
>>>  	uint32_t addr;
>>> @@ -118,6 +153,7 @@ struct jzfb {
>>>  	struct fb_info *fb;
>>>  	struct platform_device *pdev;
>>>  	void __iomem *base;
>>> +	phys_t phys_base;
>>>  	struct resource *mem;
>>>  	struct jz4740_fb_platform_data *pdata;
>>>  
>>> @@ -126,6 +162,7 @@ struct jzfb {
>>>  	dma_addr_t vidmem_phys;
>>>  	struct jzfb_framedesc *framedesc;
>>>  	dma_addr_t framedesc_phys;
>>> +	struct jz4740_dma_chan *slcd_dma;
>>>  
>>>  	struct clk *ldclk;
>>>  	struct clk *lpclk;
>>> @@ -136,6 +173,9 @@ struct jzfb {
>>>  	uint32_t pseudo_palette[16];
>>>  };
>>>  
>>> +#define JZFB_IS_SLCD(jzfb) ((jzfb)->pdata->lcd_type & (1 << 5))
>>> +#define JZFB_IS_SLCD_SERIAL_TYPE(jzfb) ((jzfb)->pdata->lcd_type & (2 << 5))
>> These two should be static inline bool.
> 
> Ok.
> 
>>> +static void send_panel_command(struct jzfb *jzfb, u32 cmd)
>> You've been using u{16,32} throughout the whole patch, since the existing code
>> uses uint_{16,32}t you should stick to them.
> 
> Ok, probably a restant from the original opendingux patch.
> 
>>> +{
>>> +	u16 slcd_cfg = readw(jzfb->base + JZ_REG_SLCD_CFG);
>>> +
>>> +	jzfb_slcd_wait(jzfb);
>>> +
>>> +	switch (slcd_cfg & JZ_SLCD_CFG_CWIDTH_MASK) {
>>> +	case JZ_SLCD_CFG_CWIDTH_8BIT:
>>> +		writel(JZ_SLCD_DATA_RS_COMMAND | ((cmd & 0xff00) >> 8),
>>> +		       jzfb->base + JZ_REG_SLCD_DATA);
>>> +		jzfb_slcd_wait(jzfb);
>>> +		writel(JZ_SLCD_DATA_RS_COMMAND | (cmd & 0xff),
>>> +		       jzfb->base + JZ_REG_SLCD_DATA);
>>> +		break;
>>> +
>>> +	case JZ_SLCD_CFG_CWIDTH_16BIT:
>>> +		writel(JZ_SLCD_DATA_RS_COMMAND | (cmd & 0xffff),
>>> +		       jzfb->base + JZ_REG_SLCD_DATA);
>>> +		break;
>>> +
>>> +	case JZ_SLCD_CFG_CWIDTH_18BIT:
>>> +		writel(JZ_SLCD_DATA_RS_COMMAND | ((cmd & 0xff00) << 2) |
>>> +		       ((cmd & 0xff) << 1), jzfb->base + JZ_REG_SLCD_DATA);
>>> +		break;
>>> +	}
>>> +}
>>> +
>>> +static void send_panel_data(struct jzfb *jzfb, u32 data)
>>> +{
>>> +	u16 slcd_cfg = readw(jzfb->base + JZ_REG_SLCD_CFG);
>>> +
>>> +	switch (slcd_cfg & JZ_SLCD_CFG_DWIDTH_MASK) {
>>> +	case JZ_SLCD_CFG_DWIDTH_18:
>>> +	case JZ_SLCD_CFG_DWIDTH_9_x2:
>>> +		data = ((data & 0xff) << 1) | ((data & 0xff00) << 2);
>>> +		data = ((data << 6) & 0xfc0000) | ((data << 4) & 0xfc00) |
>>> +		       ((data << 2) & 0x0000fc);
>>> +		break;
>>> +
>>> +	case JZ_SLCD_CFG_DWIDTH_16:
>>> +	default:
>>> +		data &= 0xffff;
>>> +		break;
>>> +	}
>>> +
>>> +	jzfb_slcd_wait(jzfb);
>>> +	writel(JZ_SLCD_DATA_RS_DATA | data, jzfb->base + JZ_REG_SLCD_DATA);
>>> +}
>>> +
>>> +void jz4740_fb_slcd_disable_transfer(struct platform_device *pdev)
>>> +{
>>> +	struct jzfb *jzfb = platform_get_drvdata(pdev);
>>> +
>>> +	mutex_lock(&jzfb->lock);
>>> +
>>> +	if (jzfb->is_enabled) {
>>> +		jz4740_dma_disable(jzfb->slcd_dma);
>>> +		jzfb_slcd_wait(jzfb);
>>> +	}
>>> +
>>> +	mutex_unlock(&jzfb->lock);
>>> +}
>>> +EXPORT_SYMBOL_GPL(jz4740_fb_slcd_disable_transfer);
>>> +
>>> +void jz4740_fb_slcd_enable_transfer(struct platform_device *pdev)
>>> +{
>>> +	struct jzfb *jzfb = platform_get_drvdata(pdev);
>>> +
>>> +	mutex_lock(&jzfb->lock);
>>> +
>>> +	if (jzfb->is_enabled)
>>> +		jzfb_slcd_start_dma(jzfb);
>>> +
>>> +	mutex_unlock(&jzfb->lock);
>>> +}
>>> +EXPORT_SYMBOL_GPL(jz4740_fb_slcd_enable_transfer);
>>> +
>>> +void jz4740_fb_slcd_send_cmd_data(struct platform_device *pdev,
>>> +				  unsigned int cmd, unsigned int data)
>>> +{
>>> +	struct jzfb *jzfb = platform_get_drvdata(pdev);
>>> +
>>> +	mutex_lock(&jzfb->lock);
>>> +
>>> +	if (!jzfb->is_enabled)
>>> +		clk_enable(jzfb->ldclk);
>>> +
>>> +	send_panel_command(jzfb, cmd);
>>> +	send_panel_data(jzfb, data);
>>> +
>>> +	if (!jzfb->is_enabled) {
>>> +		jzfb_slcd_wait(jzfb);
>>> +		clk_disable(jzfb->ldclk);
>>> +	}
>>> +
>>> +	mutex_unlock(&jzfb->lock);
>>> +}
>>> +EXPORT_SYMBOL_GPL(jz4740_fb_slcd_send_cmd_data);
>>> +
>>> +void jz4740_fb_slcd_send_cmd(struct platform_device *pdev, unsigned int cmd)
>>> +{
>>> +	struct jzfb *jzfb = platform_get_drvdata(pdev);
>>> +
>>> +	mutex_lock(&jzfb->lock);
>>> +
>>> +	if (!jzfb->is_enabled)
>>> +		clk_enable(jzfb->ldclk);
>>> +
>>> +	send_panel_command(jzfb, cmd);
>>> +
>>> +	if (!jzfb->is_enabled) {
>>> +		jzfb_slcd_wait(jzfb);
>>> +		clk_disable(jzfb->ldclk);
>>> +	}
>>> +
>>> +	mutex_unlock(&jzfb->lock);
>>> +}
>>> +EXPORT_SYMBOL_GPL(jz4740_fb_slcd_send_cmd);
>> jz4740_fb_slcd_send_cmd_data and jz4740_fb_slcd_send_cmd are almost identical
>> it might makes sense to merge them.
> 
> Something like
> void jz4740_fb_slcd_send_cmd(struct platform_device *pdev, unsigned int cmd,
> unsigned int data, bool with_data);
> ?
> 
> You can't use data == 0 as "do not send data" as this could be a valid parameter.
> 
>> I don't like the idea of adding a jzfb specific interface for talking to the
>> lcd panels, because it will tightly couple the panel driver to the framebuffer
>> driver and it won't be possible to reuse for some board using the same panel
>> but a different SoC.
>>

I've seen that you are writing quite a few registers at once in the G240400RTSW
driver. So it might makes sense to have a single function which takes an array
of uint32_t, with all these values instead of switching back and forth between
the framebuffer and panel driver.
You could encode whether a certain word is a command or data through the most
upper bit as it is done in the actual register as well.

Something like
void jz4740_fb_slcd_send_cmd(struct device *dev, const uint32_t *data, size_t num)
{
	struct jzfb *jzfb = dev_get_drvdata(dev);
	uint32_t d;

	mutex_lock(&jzfb->lock);

	if (!jzfb->is_enabled)
		clk_enable(jzfb->ldclk);

	for(; num; --num, ++data) {
		d = *data;
		if (d & JZ_SLCD_DATA_RS_CMD)
			d = jz4740_fb_slcd_panel_cmd(d);
		else
			d = jz4740_fb_slcd_panel_daya(d)
		jzfb_slcd_wait(jzfb);
		writel(d, jzfb->base + JZ_REG_SLCD_DATA);	
	}

	if (!jzfb->is_enabled) {
		jzfb_slcd_wait(jzfb);
		clk_disable(jzfb->ldclk);
	}

	mutex_unlock(&jzfb->lock);
}


> 
> Me neither, but the only "good" way forward I see is emulating an SPI interface
> over this..
> Do you think this is acceptable?
> 

Since the driver is not actually talking SPI over the bus I don't think this is
an option.
What might be an option is to provide a generic callback structure for the
panel driver.

struct panel_platform_data {
	void (*panel_write)(void *data, const uint32_t *data, size_t num);
	void *panel_data;
};

So that the panel driver doesn't reference the jzfb driver directly.

- Lars



--
To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Video for Linux]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Tourism]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux