/*
 * ILI9486 LCD Framebuffer Driver for Orange Pi PC
 * 基于fbtft框架的ILI9486驱动示例
 * 
 * 硬件连接（SPI模式）：
 *   LCD_CS   → SPI0_CS0
 *   LCD_RS   → GPIO DC
 *   LCD_SCK  → SPI0_SCK
 *   LCD_SI   → SPI0_MOSI
 *   RST      → GPIO RESET
 *   LED      → 3.3V或PWM
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/fb.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <video/mipi_display.h>

/* ==================== ILI9486 寄存器定义 ==================== */
#define ILI9486_NOP           0x00  // 空操作
#define ILI9486_SWRESET       0x01  // 软件复位
#define ILI9486_SLPOUT        0x11  // 退出睡眠
#define ILI9486_DISPOFF       0x28  // 关显示
#define ILI9486_DISPON        0x29  // 开显示
#define ILI9486_CASET         0x2A  // 列地址设置
#define ILI9486_PASET         0x2B  // 行地址设置
#define ILI9486_RAMWR         0x2C  // 写GRAM
#define ILI9486_RAMRD         0x2E  // 读GRAM
#define ILI9486_MADCTL        0x36  // 屏幕旋转/镜像控制
#define ILI9486_PIXFMT        0x3A  // 像素格式（16/18/24位）

/* ==================== 驱动私有数据 ==================== */
struct ili9486_priv {
    struct spi_device *spi;
    struct gpio_desc *dc;      // 数据/命令选择
    struct gpio_desc *reset;   // 复位
    struct gpio_desc *led;     // 背光
    u16 *txbuf;                // SPI发送缓冲区
    u16 *fbmem;                // 帧缓冲内存
    int rotate;                // 旋转角度 0/90/180/270
    int width;                 // 当前宽度
    int height;                // 当前高度
};

/* ==================== 底层SPI操作 ==================== */

/*
 * 发送命令字节
 * DC引脚拉低表示命令
 */
static int ili9486_write_cmd(struct ili9486_priv *priv, u8 cmd)
{
    gpiod_set_value(priv->dc, 0);  // DC=0 表示命令
    return spi_write(priv->spi, &cmd, 1);
}

/*
 * 发送单个数据字节
 * DC引脚拉高表示数据
 */
static int ili9486_write_data(struct ili9486_priv *priv, u8 data)
{
    gpiod_set_value(priv->dc, 1);  // DC=1 表示数据
    return spi_write(priv->spi, &data, 1);
}

/*
 * 发送多个数据字节
 * 用于批量传输像素数据，提高效率
 */
static int ili9486_write_data_buf(struct ili9486_priv *priv, 
                                   const u8 *buf, size_t len)
{
    gpiod_set_value(priv->dc, 1);  // DC=1 表示数据
    return spi_write(priv->spi, buf, len);
}

/*
 * 发送16位数据（大端序）
 * ILI9486的16位像素数据是大端序：高字节先发
 */
static int ili9486_write_data16(struct ili9486_priv *priv, u16 data)
{
    u8 buf[2] = { data >> 8, data & 0xFF };
    return ili9486_write_data_buf(priv, buf, 2);
}

/* ==================== ILI9486 初始化序列 ==================== */

/*
 * ILI9486初始化序列
 * 这是针对常见3.5寸RGB屏的初始化
 * 不同厂家的屏可能需要调整
 */
static int ili9486_init_sequence(struct ili9486_priv *priv)
{
    int ret;
    
    /* 硬件复位 */
    if (priv->reset) {
        gpiod_set_value(priv->reset, 1);
        msleep(20);
        gpiod_set_value(priv->reset, 0);
        msleep(20);
        gpiod_set_value(priv->reset, 1);
        msleep(120);
    }
    
    /* 软件复位 */
    ret = ili9486_write_cmd(priv, ILI9486_SWRESET);
    if (ret) return ret;
    msleep(150);
    
    /* 退出睡眠模式 */
    ret = ili9486_write_cmd(priv, ILI9486_SLPOUT);
    if (ret) return ret;
    msleep(50);
    
    /* -------- ILI9486特殊指令集 -------- */
    
    /* 电源控制1 */
    ili9486_write_cmd(priv, 0xC0);
    ili9486_write_data(priv, 0x23);  // VRH: 4.60V
    
    /* 电源控制2 */
    ili9486_write_cmd(priv, 0xC1);
    ili9486_write_data(priv, 0x10);  // SAP:2.5V; BT:0x00
    
    /* VCOM控制1 */
    ili9486_write_cmd(priv, 0xC5);
    ili9486_write_data(priv, 0x3E);  // VCM: 2.85V
    ili9486_write_data(priv, 0x28);  // VCOM: -1.00V
    
    /* VCOM控制2 */
    ili9486_write_cmd(priv, 0xC7);
    ili9486_write_data(priv, 0x86);  // VCOMH: 4.300V
    
    /* 像素格式：16位（RGB565） */
    ili9486_write_cmd(priv, ILI9486_PIXFMT);
    ili9486_write_data(priv, 0x55);  // 16位/pixel
    
    /* 帧率控制：79Hz */
    ili9486_write_cmd(priv, 0xB1);
    ili9486_write_data(priv, 0x00);
    ili9486_write_data(priv, 0x18);  // 分频比
    
    /* 显示功能控制 */
    ili9486_write_cmd(priv, 0xB6);
    ili9486_write_data(priv, 0x08);  // 立即刷新
    ili9486_write_data(priv, 0x82);  // 非显示区域电位：VCOM
    ili9486_write_data(priv, 0x27);  // 320行
    
    /* Gamma校正 (正面) */
    ili9486_write_cmd(priv, 0xE0);
    ili9486_write_data(priv, 0x0F);
    ili9486_write_data(priv, 0x31);
    ili9486_write_data(priv, 0x2B);
    ili9486_write_data(priv, 0x0C);
    ili9486_write_data(priv, 0x0E);
    ili9486_write_data(priv, 0x08);
    ili9486_write_data(priv, 0x4E);
    ili9486_write_data(priv, 0xF1);
    ili9486_write_data(priv, 0x37);
    ili9486_write_data(priv, 0x07);
    ili9486_write_data(priv, 0x10);
    ili9486_write_data(priv, 0x03);
    ili9486_write_data(priv, 0x0E);
    ili9486_write_data(priv, 0x09);
    ili9486_write_data(priv, 0x00);
    
    /* Gamma校正 (背面) */
    ili9486_write_cmd(priv, 0xE1);
    ili9486_write_data(priv, 0x00);
    ili9486_write_data(priv, 0x0E);
    ili9486_write_data(priv, 0x14);
    ili9486_write_data(priv, 0x03);
    ili9486_write_data(priv, 0x11);
    ili9486_write_data(priv, 0x07);
    ili9486_write_data(priv, 0x31);
    ili9486_write_data(priv, 0xC1);
    ili9486_write_data(priv, 0x48);
    ili9486_write_data(priv, 0x08);
    ili9486_write_data(priv, 0x0F);
    ili9486_write_data(priv, 0x0C);
    ili9486_write_data(priv, 0x31);
    ili9486_write_data(priv, 0x36);
    ili9486_write_data(priv, 0x0F);
    
    /* 设置屏幕旋转 */
    ili9486_write_cmd(priv, ILI9486_MADCTL);
    switch (priv->rotate) {
    case 0:
        ili9486_write_data(priv, 0x48);  // 竖屏
        break;
    case 90:
        ili9486_write_data(priv, 0x28);  // 横屏
        break;
    case 180:
        ili9486_write_data(priv, 0x88);  // 竖屏翻转
        break;
    case 270:
        ili9486_write_data(priv, 0xE8);  // 横屏翻转
        break;
    default:
        ili9486_write_data(priv, 0x48);
        break;
    }
    
    /* 开显示 */
    ret = ili9486_write_cmd(priv, ILI9486_DISPON);
    if (ret) return ret;
    msleep(20);
    
    /* 开背光 */
    if (priv->led)
        gpiod_set_value(priv->led, 1);
    
    return 0;
}

/* ==================== 帧缓冲操作 ==================== */

/*
 * 设置显示窗口
 * 所有像素操作前都需要先设置窗口
 */
static void ili9486_set_window(struct ili9486_priv *priv,
                                int x0, int y0, int x1, int y1)
{
    /* 列地址 */
    ili9486_write_cmd(priv, ILI9486_CASET);
    ili9486_write_data16(priv, x0);
    ili9486_write_data16(priv, x1);
    
    /* 行地址 */
    ili9486_write_cmd(priv, ILI9486_PASET);
    ili9486_write_data16(priv, y0);
    ili9486_write_data16(priv, y1);
    
    /* 准备写GRAM */
    ili9486_write_cmd(priv, ILI9486_RAMWR);
}

/*
 * fbtft回调：写屏幕
 * 当framebuffer内容改变时调用
 */
static void ili9486_fb_write(struct fb_info *info, 
                              int x, int y, int w, int h)
{
    struct ili9486_priv *priv = info->par;
    int i, j;
    u16 *fbmem = (u16 *)info->screen_base;
    int fb_width = info->var.xres;
    
    /* 设置显示窗口 */
    ili9486_set_window(priv, x, y, x + w - 1, y + h - 1);
    
    /* 发送像素数据 */
    gpiod_set_value(priv->dc, 1);  // DC=1 表示数据
    
    for (j = y; j < y + h; j++) {
        for (i = x; i < x + w; i++) {
            u16 pixel = fbmem[j * fb_width + i];
            u8 buf[2] = { pixel >> 8, pixel & 0xFF };
            spi_write(priv->spi, buf, 2);
        }
    }
}

/* ==================== Platform驱动 ==================== */

static int ili9486_probe(struct spi_device *spi)
{
    struct ili9486_priv *priv;
    struct fb_info *info;
    int ret;
    
    /* 分配驱动私有数据 */
    priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;
    
    priv->spi = spi;
    priv->rotate = 270;  // 默认横屏
    
    /* 获取GPIO */
    priv->dc = devm_gpiod_get(&spi->dev, "dc", GPIOD_OUT_LOW);
    if (IS_ERR(priv->dc))
        return dev_err_probe(&spi->dev, PTR_ERR(priv->dc), "Failed to get DC GPIO");
    
    priv->reset = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_LOW);
    if (IS_ERR(priv->reset))
        priv->reset = NULL;  // 复位GPIO可选
    
    priv->led = devm_gpiod_get(&spi->dev, "led", GPIOD_OUT_LOW);
    if (IS_ERR(priv->led))
        priv->led = NULL;  // 背光GPIO可选
    
    /* 配置SPI */
    spi->mode = SPI_MODE_0;
    spi->bits_per_word = 8;
    spi->max_speed_hz = 32000000;  // 32MHz
    ret = spi_setup(spi);
    if (ret)
        return dev_err_probe(&spi->dev, ret, "SPI setup failed");
    
    /* 分配framebuffer */
    info = framebuffer_alloc(sizeof(struct ili9486_priv), &spi->dev);
    if (!info)
        return -ENOMEM;
    
    info->par = priv;
    
    /* 设置fb_fix_screeninfo */
    strcpy(info->fix.id, "ili9486");
    info->fix.type = FB_TYPE_PACKED_PIXELS;
    info->fix.visual = FB_VISUAL_TRUECOLOR;
    info->fix.line_length = 320 * 2;  // 320像素 × 2字节/像素
    info->fix.smem_len = 320 * 480 * 2;  // 总显存大小
    
    /* 设置fb_var_screeninfo */
    info->var.xres = 320;
    info->var.yres = 480;
    info->var.xres_virtual = 320;
    info->var.yres_virtual = 480;
    info->var.bits_per_pixel = 16;
    
    /* RGB565格式 */
    info->var.red.offset = 11;
    info->var.red.length = 5;
    info->var.green.offset = 5;
    info->var.green.length = 6;
    info->var.blue.offset = 0;
    info->var.blue.length = 5;
    
    /* 分配显存 */
    info->screen_base = vmalloc(info->fix.smem_len);
    if (!info->screen_base) {
        ret = -ENOMEM;
        goto err_fb_free;
    }
    memset(info->screen_base, 0, info->fix.smem_len);
    
    /* 初始化ILI9486 */
    ret = ili9486_init_sequence(priv);
    if (ret) {
        dev_err(&spi->dev, "Failed to initialize ILI9486\n");
        goto err_fb_vfree;
    }
    
    /* 注册framebuffer */
    ret = register_framebuffer(info);
    if (ret) {
        dev_err(&spi->dev, "Failed to register framebuffer\n");
        goto err_fb_vfree;
    }
    
    spi_set_drvdata(spi, info);
    
    dev_info(&spi->dev, "ILI9486 LCD initialized: %dx%d@%dfps\n",
             info->var.xres, info->var.yres, 30);
    
    return 0;

err_fb_vfree:
    vfree(info->screen_base);
err_fb_free:
    framebuffer_release(info);
    return ret;
}

static void ili9486_remove(struct spi_device *spi)
{
    struct fb_info *info = spi_get_drvdata(spi);
    
    if (info) {
        unregister_framebuffer(info);
        vfree(info->screen_base);
        framebuffer_release(info);
    }
}

/* 设备树匹配表 */
static const struct of_device_id ili9486_of_match[] = {
    { .compatible = "ilitek,ili9486" },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ili9486_of_match);

/* SPI驱动结构体 */
static struct spi_driver ili9486_driver = {
    .driver = {
        .name = "ili9486",
        .of_match_table = ili9486_of_match,
    },
    .probe = ili9486_probe,
    .remove = ili9486_remove,
};

module_spi_driver(ili9486_driver);

MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("ILI9486 TFT LCD Framebuffer Driver");
MODULE_LICENSE("GPL");
