// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Epson HWA742 LCD controller driver
*
* Copyright (C) 2004-2005 Nokia Corporation
* Authors: Juha Yrjölä <juha.yrjola@nokia.com>
* Imre Deak <imre.deak@nokia.com>
* YUV support: Jussi Laako <jussi.laako@nokia.com>
*/
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/fb.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include "omapfb.h"
#define HWA742_REV_CODE_REG 0x0
#define HWA742_CONFIG_REG 0x2
#define HWA742_PLL_DIV_REG 0x4
#define HWA742_PLL_0_REG 0x6
#define HWA742_PLL_1_REG 0x8
#define HWA742_PLL_2_REG 0xa
#define HWA742_PLL_3_REG 0xc
#define HWA742_PLL_4_REG 0xe
#define HWA742_CLK_SRC_REG 0x12
#define HWA742_PANEL_TYPE_REG 0x14
#define HWA742_H_DISP_REG 0x16
#define HWA742_H_NDP_REG 0x18
#define HWA742_V_DISP_1_REG 0x1a
#define HWA742_V_DISP_2_REG 0x1c
#define HWA742_V_NDP_REG 0x1e
#define HWA742_HS_W_REG 0x20
#define HWA742_HP_S_REG 0x22
#define HWA742_VS_W_REG 0x24
#define HWA742_VP_S_REG 0x26
#define HWA742_PCLK_POL_REG 0x28
#define HWA742_INPUT_MODE_REG 0x2a
#define HWA742_TRANSL_MODE_REG1 0x2e
#define HWA742_DISP_MODE_REG 0x34
#define HWA742_WINDOW_TYPE 0x36
#define HWA742_WINDOW_X_START_0 0x38
#define HWA742_WINDOW_X_START_1 0x3a
#define HWA742_WINDOW_Y_START_0 0x3c
#define HWA742_WINDOW_Y_START_1 0x3e
#define HWA742_WINDOW_X_END_0 0x40
#define HWA742_WINDOW_X_END_1 0x42
#define HWA742_WINDOW_Y_END_0 0x44
#define HWA742_WINDOW_Y_END_1 0x46
#define HWA742_MEMORY_WRITE_LSB 0x48
#define HWA742_MEMORY_WRITE_MSB 0x49
#define HWA742_MEMORY_READ_0 0x4a
#define HWA742_MEMORY_READ_1 0x4c
#define HWA742_MEMORY_READ_2 0x4e
#define HWA742_POWER_SAVE 0x56
#define HWA742_NDP_CTRL 0x58
#define HWA742_AUTO_UPDATE_TIME (HZ / 20)
/* Reserve 4 request slots for requests in irq context */
#define REQ_POOL_SIZE 24
#define IRQ_REQ_POOL_SIZE 4
#define REQ_FROM_IRQ_POOL 0x01
#define REQ_COMPLETE 0
#define REQ_PENDING 1
struct update_param {
int x, y, width, height;
int color_mode;
int flags;
};
struct hwa742_request {
struct list_head entry;
unsigned int flags;
int (*handler)(struct hwa742_request *req);
void (*complete)(void *data);
void *complete_data;
union {
struct update_param update;
struct completion *sync;
} par;
};
struct {
enum omapfb_update_mode update_mode;
enum omapfb_update_mode update_mode_before_suspend;
struct timer_list auto_update_timer;
int stop_auto_update;
struct omapfb_update_window auto_update_window;
unsigned te_connected:1;
unsigned vsync_only:1;
struct hwa742_request req_pool[REQ_POOL_SIZE];
struct list_head pending_req_list;
struct list_head free_req_list;
/*
* @req_lock: protect request slots pool and its tracking lists
* @req_sema: counter; slot allocators from task contexts must
* push it down before acquiring a slot. This
* guarantees that atomic contexts will always have
* a minimum of IRQ_REQ_POOL_SIZE slots available.
*/
struct semaphore req_sema;
spinlock_t req_lock;
struct extif_timings reg_timings, lut_timings;
int prev_color_mode;
int prev_flags;
int window_type;
u32 max_transmit_size;
u32 extif_clk_period;
unsigned long pix_tx_time;
unsigned long line_upd_time;
struct omapfb_device *fbdev;
struct lcd_ctrl_extif *extif;
const struct lcd_ctrl *int_ctrl;
struct clk *sys_ck;
} hwa742;
struct lcd_ctrl hwa742_ctrl;
static u8 hwa742_read_reg(u8 reg)
{
u8 data;
hwa742.extif->set_bits_per_cycle(8);
hwa742.extif->write_command(®, 1);
hwa742.extif->read_data(&data, 1);
return data;
}
static void hwa742_write_reg(u8 reg, u8 data)
{
hwa742.extif->set_bits_per_cycle(8);
hwa742.extif->write_command(®, 1);
hwa742.extif->write_data(&data, 1);
}
static void set_window_regs(int x_start, int y_start, int x_end, int y_end)
{
u8 tmp[8];
u8 cmd;
x_end--;
y_end--;
tmp[0] = x_start;
tmp[1] = x_start >> 8;
tmp[2] = y_start;
tmp[3] = y_start >> 8;
tmp[4] = x_end;
tmp[5] = x_end >> 8;
tmp[6] = y_end;
tmp[7] = y_end >> 8;
hwa742.extif->set_bits_per_cycle(8);
cmd = HWA742_WINDOW_X_START_0;
hwa742.extif->write_command(&cmd, 1);
hwa742.extif->write_data(tmp, 8);
}
static void set_format_regs(int conv, int transl, int flags)
{
if (flags & OMAPFB_FORMAT_FLAG_DOUBLE) {
hwa742.window_type = ((hwa742.window_type & 0xfc) | 0x01);
#ifdef VERBOSE
dev_dbg(hwa742.fbdev->dev, "hwa742: enabled pixel doubling\n");
#endif
} else {
hwa742.window_type = (hwa742.window_type & 0xfc);
#ifdef VERBOSE
dev_dbg(hwa742.fbdev->dev, "hwa742: disabled pixel doubling\n");
#endif
}
hwa742_write_reg(HWA742_INPUT_MODE_REG, conv);
hwa742_write_reg(HWA742_TRANSL_MODE_REG1, transl);
hwa742_write_reg(HWA742_WINDOW_TYPE, hwa742.window_type);
}
static void enable_tearsync(int y, int width, int height, int screen_height,
int force_vsync)
{
u8 b;
b = hwa742_read_reg(HWA742_NDP_CTRL);
b |= 1 << 2;
hwa742_write_reg(HWA742_NDP_CTRL, b);
if (likely(hwa742.vsync_only || force_vsync)) {
hwa742.extif->enable_tearsync(1, 0);
return;
}
if