/* $NetBSD: ssdfbvar.h,v 1.10 2021/08/05 22:31:20 tnn Exp $ */

/*
* Copyright (c) 2019 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Tobias Nygren.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

/*
* cfdata attachment flags
*/
#define SSDFB_ATTACH_FLAG_PRODUCT_MASK          0x000000ff
#define SSDFB_ATTACH_FLAG_UPSIDEDOWN            0x00000100
#define SSDFB_ATTACH_FLAG_INVERSE               0x00000200
#define SSDFB_ATTACH_FLAG_CONSOLE               0x00000400
#define SSDFB_ATTACH_FLAG_MPSAFE                0x00000800

/*
* Fundamental commands
* SSD1306 Rev 1.1 p.28
* SH1106 Rev 0.1 p.19,20,22
*/
#define SSDFB_CMD_SET_CONTRAST_CONTROL          0x81
#define SSDFB_CMD_ENTIRE_DISPLAY_OFF            0xa4
#define SSDFB_CMD_ENTIRE_DISPLAY_ON             0xa5
#define SSDFB_CMD_SET_NORMAL_DISPLAY            0xa6
#define SSDFB_CMD_SET_INVERSE_DISPLAY           0xa7
#define SSDFB_CMD_SET_DISPLAY_OFF               0xae
#define SSDFB_CMD_SET_DISPLAY_ON                0xaf

/*
* Scrolling commands; SSD1306 Rev 1.1 p. 28
*/
#define SSDFB_CMD_VERTICAL_AND_RIGHT_SCROLL     0x29
#define SSDFB_CMD_VERTICAL_AND_LEFT_SCROLL      0x2a
#define SSDFB_CMD_DEACTIVATE_SCROLL             0x2e
#define SSDFB_CMD_ACTIVATE_SCROLL               0x2f
#define SSDFB_CMD_SET_VERTICAL_SCROLL_AREA      0xa3

/*
* Addressing commands
* SSD1306 Rev 1.1 p.30
* SH1106 Rev 0.1 p.18,22
*/
#define SSDFB_CMD_SET_LOWER_COLUMN_START_ADDRESS_BASE   0x00
#define SSDFB_CMD_SET_LOWER_COLUMN_START_ADDRESS_MAX    0x0f
#define SSDFB_CMD_SET_HIGHER_COLUMN_START_ADDRESS_BASE  0x10
#define SSDFB_CMD_SET_HIGHER_COLUMN_START_ADDRESS_MAX   0x1f
#define SSD1306_CMD_SET_MEMORY_ADDRESSING_MODE          0x20
       #define SSD1306_MEMORY_ADDRESSING_MODE_HORIZONTAL 0x00
       #define SSD1306_MEMORY_ADDRESSING_MODE_VERTICAL 0x01
       #define SSD1306_MEMORY_ADDRESSING_MODE_PAGE     0x02
#define SSD1306_CMD_SET_COLUMN_ADDRESS                  0x21
#define SSD1306_CMD_SET_PAGE_ADDRESS                    0x22
#define SSDFB_CMD_SET_PAGE_START_ADDRESS_BASE           0xb0
#define SSDFB_CMD_SET_PAGE_START_ADDRESS_MAX            0xb7

/*
* Resolution & hardware layout commands
* SSD1306 Rev 1.1 p.31
* SH1106 Rev 0.1 p.19,20,21,23
*/
#define SSDFB_CMD_SET_DISPLAY_START_LINE_BASE           0x40
#define SSDFB_CMD_SET_DISPLAY_START_LINE_MAX            0x7f
#define SSDFB_CMD_SET_SEGMENT_REMAP_NORMAL              0xa0
#define SSDFB_CMD_SET_SEGMENT_REMAP_REVERSE             0xa1
#define SSDFB_CMD_SET_MULTIPLEX_RATIO                   0xa8
#define SSDFB_CMD_SET_COM_OUTPUT_DIRECTION_NORMAL       0xc0
#define SSDFB_CMD_SET_COM_OUTPUT_DIRECTION_REMAP        0xc8
#define SSDFB_CMD_SET_DISPLAY_OFFSET                    0xd3
#define SSDFB_CMD_SET_COM_PINS_HARDWARE_CFG             0xda
       #define SSDFB_COM_PINS_A1_MASK                  0x02
       #define SSDFB_COM_PINS_ALTERNATIVE_MASK         0x10
       #define SSDFB_COM_PINS_REMAP_MASK               0x20

/*
* Timing & driving commands
* SSD1306 Rev 1.1 p.32
* SH1106 Rev 0.1 p.24,25,26
*/
#define SSDFB_CMD_SET_DISPLAY_CLOCK_RATIO               0xd5
       #define SSDFB_DISPLAY_CLOCK_DIVIDER_MASK        __BITS(3, 0)
       #define SSDFB_DISPLAY_CLOCK_OSCILLATOR_MASK     __BITS(7, 4)
#define SSDFB_CMD_SET_PRECHARGE_PERIOD                  0xd9
       #define SSDFB_PRECHARGE_MASK                    __BITS(3, 0)
       #define SSDFB_DISCHARGE_MASK                    __BITS(7, 4)
#define SSDFB_CMD_SET_VCOMH_DESELECT_LEVEL              0xdb
       #define SSD1306_VCOMH_DESELECT_LEVEL_0_65_VCC   0x00
       #define SSD1306_VCOMH_DESELECT_LEVEL_0_77_VCC   0x20
       #define SSD1306_VCOMH_DESELECT_LEVEL_0_83_VCC   0x30
       #define SH1106_VCOMH_DESELECT_LEVEL_DEFAULT     0x35

/*
* Misc commands
* SSD1306 Rev 1.1 p.32
* SH1106 Rev 0.1 p.27,28
*/
#define SSDFB_CMD_NOP                                   0xe3
#define SH1106_CMD_READ_MODIFY_WRITE                    0xe0
#define SH1106_CMD_READ_MODIFY_WRITE_CANCEL             0xee

/*
* Charge pump commands
* SSD1306 App Note Rev 0.4 p.3
* SH1106 V0.1 p.18
*/
#define SSD1306_CMD_SET_CHARGE_PUMP                     0x8d
       #define SSD1306_CHARGE_PUMP_ENABLE              0x14
       #define SSD1306_CHARGE_PUMP_DISABE              0x10
#define SH1106_CMD_SET_CHARGE_PUMP_7V4                  0x30
#define SH1106_CMD_SET_CHARGE_PUMP_8V0                  0x31
#define SH1106_CMD_SET_CHARGE_PUMP_8V4                  0x32
#define SH1106_CMD_SET_CHARGE_PUMP_9V0                  0x33

/*
* DC-DC commands
* SH1106 V0.1 p.18
*/
#define SH1106_CMD_SET_DC_DC                            0xad
       #define SH1106_DC_DC_OFF                        0x8a
       #define SH1106_DC_DC_ON                         0x8b

/*
* SSD1322 command set
*/
#define SSD1322_CMD_ENABLE_GRAY_SCALE_TABLE             0x00
#define SSD1322_CMD_SET_COLUMN_ADDRESS                  0x15
#define SSD1322_CMD_WRITE_RAM                           0x5c
#define SSD1322_CMD_READ_RAM                            0x5d
#define SSD1322_CMD_SET_ROW_ADDRESS                     0x75
#define SSD1322_CMD_SET_REMAP_AND_DUAL_COM_LINE_MODE    0xa0
#define SSD1322_CMD_SET_DISPLAY_START_LINE              0xa1
#define SSD1322_CMD_SET_DISPLAY_OFFSET                  0xa2

#define SSD1322_CMD_ENTIRE_DISPLAY_OFF                  SSDFB_CMD_ENTIRE_DISPLAY_OFF
#define SSD1322_CMD_ENTIRE_DISPLAY_ON                   SSDFB_CMD_ENTIRE_DISPLAY_ON
#define SSD1322_CMD_NORMAL_DISPLAY                      SSDFB_CMD_SET_NORMAL_DISPLAY
#define SSD1322_CMD_INVERSE_DISPLAY                     SSDFB_CMD_SET_INVERSE_DISPLAY
#define SSD1322_CMD_SET_SLEEP_MODE_ON                   SSDFB_CMD_SET_DISPLAY_OFF
#define SSD1322_CMD_SET_SLEEP_MODE_OFF                  SSDFB_CMD_SET_DISPLAY_ON

#define SSD1322_CMD_ENABLE_PARTIAL_DISPLAY              0xa8
#define SSD1322_CMD_EXIT_PARTIAL_DISPLAY                0xa9
#define SSD1322_CMD_FUNCTION_SELECTION                  0xab
       #define SSD1322_FUNCTION_SELECTION_EXTERNAL_VDD 0
       #define SSD1322_FUNCTION_SELECTION_INTERNAL_VDD __BIT(0)
#define SSD1322_CMD_SET_PHASE_LENGTH                    0xb1
       #define SSD1322_PHASE_LENGTH_PHASE_2_MASK       __BITS(7, 4)
       #define SSD1322_DEFAULT_PHASE_2                 7
       #define SSD1322_PHASE_LENGTH_PHASE_1_MASK       __BITS(3, 0)
       #define SSD1322_DEFAULT_PHASE_1                 4
#define SSD1322_CMD_SET_FRONT_CLOCK_DIVIDER             0xb3
       #define SSD1322_FREQUENCY_MASK                  __BITS(7, 4)
       #define SSD1322_DEFAULT_FREQUENCY               5
       #define SSD1322_DIVIDER_MASK                    __BITS(3, 0)
       #define SSD1322_DEFAULT_DIVIDER                 0
#define SSD1322_CMD_DISPLAY_ENHANCEMENT_A               0xb4
       #define SSD1322_DISPLAY_ENHANCEMENT_A_MAGIC1    0xa2
       #define SSD1322_DISPLAY_ENHANCEMENT_A_MAGIC2    0xb5
#define SSD1322_CMD_SET_GPIO                            0xb5
       #define SSD1322_GPIO0_DISABLED                  0
       #define SSD1322_GPIO0_TRISTATE                  __BIT(0)
       #define SSD1322_GPIO0_LOW                       __BIT(1)
       #define SSD1322_GPIO0_HIGH                      __BITS(1, 0)
       #define SSD1322_GPIO1_DISABLED                  0
       #define SSD1322_GPIO1_TRISTATE                  __BIT(2)
       #define SSD1322_GPIO1_LOW                       __BIT(3)
       #define SSD1322_GPIO1_HIGH                      __BITS(3, 2)
#define SSD1322_CMD_SET_SECOND_PRECHARGE_PERIOD         0xb6
       #define SSD1322_DEFAULT_SECOND_PRECHARGE_PERIOD 8
#define SSD1322_CMD_SET_GRAY_SCALE_TABLE                0xb8
#define SSD1322_CMD_SET_DEFAULT_GRAY_SCALE_TABLE        0xb9
#define SSD1322_CMD_SET_PRE_CHARGE_VOLTAGE_LEVEL        0xbb
       #define SSD1322_DEFAULT_PRE_CHARGE_VOLTAGE_LEVEL        0x17
#define SSD1322_CMD_SET_VCOMH                           0xbe
       #define SSD1322_DEFAULT_VCOMH                   0x04
#define SSD1322_CMD_SET_CONTRAST_CURRENT                0xc1
       #define SSD1322_DEFAULT_CONTRAST_CURRENT        0x7f
#define SSD1322_CMD_MASTER_CONTRAST_CURRENT_CONTROL     0xc7
       #define SSD1322_DEFAULT_MASTER_CONTRAST_CURRENT_CONTROL 0xf
#define SSD1322_CMD_SET_MULTIPLEX_RATIO                 0xca
#define SSD1322_CMD_DISPLAY_ENHANCEMENT_B               0xd1
       #define SSD1322_DISPLAY_ENHANCEMENT_B_MAGIC1    0xa2
       #define SSD1322_DISPLAY_ENHANCEMENT_B_MAGIC2    0x20
#define SSD1322_CMD_SET_COMMAND_LOCK                    0xfd
       #define SSD1322_COMMAND_UNLOCK_MAGIC            0x12
       #define SSD1322_COMMAND_LOCK_MAGIC              0x16
/* undocumented on this chip, but works in practice */
#define SSD1322_CMD_NOP                                 SSDFB_CMD_NOP

/*
* SSD1353 command set
*/
#define SSD1353_CMD_SET_COLUMN_ADDRESS                  SSD1322_CMD_SET_COLUMN_ADDRESS
#define SSD1353_CMD_DRAW_LINE                           0x21
#define SSD1353_CMD_DRAW_RECTANGLE                      0x22
#define SSD1353_CMD_COPY                                0x23
#define SSD1353_CMD_DIM                                 0x24
#define SSD1353_CMD_CLEAR_WINDOW                        0x25
#define SSD1353_CMD_FILL_ENABLE                         0x26
#define SSD1353_CMD_SCROLLING_SETUP                     0x27
#define SSD1353_CMD_DEACTIVATE_SCROLL                   SSDFB_CMD_DEACTIVATE_SCROLL
#define SSD1353_CMD_ACTIVATE_SCROLL                     SSDFB_CMD_ACTIVATE_SCROLL
#define SSD1353_CMD_WRITE_RAM                           SSD1322_CMD_WRITE_RAM
#define SD1353_CMD_READ_RAM                             SSD1322_CMD_READ_RAM
#define SSD1353_CMD_SET_ROW_ADDRESS                     SSD1322_CMD_SET_ROW_ADDRESS
#define SSD1353_CMD_SET_CONTRAST_CONTROL_A              0x81
#define SSD1353_CMD_SET_CONTRAST_CONTROL_B              0x82
#define SSD1353_CMD_SET_CONTRAST_CONTROL_C              0x83
       #define SSD1353_DEFAULT_CONTRAST_CONTROL        128
#define SSD1353_CMD_MASTER_CURRENT_CONTROL              0x87
       #define SSD1353_DEFAULT_MASTER_CURRENT_ATTENUATION      15
#define SSD1353_CMD_SET_SECOND_PRECHARGE_SPEED          0x8a
       #define SSD1353_DEFAULT_SECOND_PRECHARGE_SPEED  2
#define SSD1353_CMD_REMAP_COLOR_DEPTH                   0xa0
       #define SSD1353_REMAP_NO_INCREMENT              __BIT(0)
       #define SSD1353_REMAP_SEG_DIRECTION             __BIT(1)
       #define SSD1353_REMAP_RGB                       __BIT(2)
       #define SSD1353_REMAP_LR                        __BIT(3)
       #define SSD1353_REMAP_COM_DIRECTION             __BIT(4)
       #define SSD1353_REMAP_SPLIT_ODD_EVEN            __BIT(5)
       #define SSD1353_REMAP_PIXEL_FORMAT_MASK         __BITS(7, 6)
#define SSD1353_CMD_SET_DISPLAY_START_LINE              SSD1322_CMD_SET_DISPLAY_START_LINE
#define SSD1353_CMD_SET_DISPLAY_OFFSET                  SSD1322_CMD_SET_DISPLAY_OFFSET
#define SSD1353_CMD_SET_VERTICAL_SCROLL_AREA            SSDFB_CMD_SET_VERTICAL_SCROLL_AREA
#define SSD1353_CMD_NORMAL_DISPLAY                      0xa4
#define SSD1353_CMD_ENTIRE_DISPLAY_ON                   0xa5
#define SSD1353_CMD_ENTIRE_DISPLAY_OFF                  0xa6
#define SSD1353_CMD_INVERSE_DISPLAY                     SSDFB_CMD_SET_INVERSE_DISPLAY
#define SSD1353_CMD_SET_MULTIPLEX_RATIO                 SSDFB_CMD_SET_MULTIPLEX_RATIO
#define SSD1353_CMD_DIM_MODE_SETTING                    0xab
#define SSD1353_CMD_SET_DISPLAY_ON_DIM                  0xac
#define SSD1353_CMD_SET_DISPLAY_OFF                     SSDFB_CMD_SET_DISPLAY_OFF
#define SSD1353_CMD_SET_DISPLAY_ON                      SSDFB_CMD_SET_DISPLAY_ON
#define SSD1353_CMD_SET_PHASE_LENGTH                    SSD1322_CMD_SET_PHASE_LENGTH
       #define SSD1353_DEFAULT_PHASE_2                 7
       #define SSD1353_DEFAULT_PHASE_1                 4
#define SSD1353_CMD_SET_FRONT_CLOCK_DIVIDER             SSD1322_CMD_SET_FRONT_CLOCK_DIVIDER
       #define SSD1353_DEFAULT_DIVIDER                 0
       #define SSD1353_DEFAULT_FREQUENCY               12
#define SSD1353_CMD_SET_SECOND_PRECHARGE_PERIOD         0xb4
       #define SSD1353_DEFAULT_SECOND_PRECHARGE_PERIOD 7
#define SSD1353_CMD_SET_GRAY_SCALE_TABLE                SSD1322_CMD_SET_GRAY_SCALE_TABLE
#define SSD1353_CMD_SET_DEFAULT_GRAY_SCALE_TABLE        SSD1322_CMD_SET_DEFAULT_GRAY_SCALE_TABLE
#define SSD1353_CMD_SET_PRE_CHARGE_VOLTAGE_LEVEL        SSD1322_CMD_SET_PRE_CHARGE_VOLTAGE_LEVEL
       #define SSD1353_DEFAULT_PRE_CHARGE_VOLTAGE_LEVEL        0x3e
#define SSD1353_CMD_SET_VCOMH                           SSD1322_CMD_SET_VCOMH
       #define SSD1353_DEFAULT_VCOMH                   0x3c
#define SSD1353_CMD_OTP_WRITE                           0xc0
#define SSD1353_CMD_RESET                               0xe2
#define SSD1353_CMD_NOP                                 SSDFB_CMD_NOP
#define SSD1353_CMD_SET_COMMAND_LOCK                    SSD1322_CMD_SET_COMMAND_LOCK
       #define SSD1353_COMMAND_UNLOCK_MAGIC            SSD1322_COMMAND_UNLOCK_MAGIC
       #define SSD1353_COMMAND_LOCK_MAGIC              SSD1353_COMMAND_LOCK_MAGIC

struct ssdfb_softc;

typedef enum {
       SSDFB_CONTROLLER_UNKNOWN=0,
       SSDFB_CONTROLLER_SSD1306=1,
       SSDFB_CONTROLLER_SH1106=2,
       SSDFB_CONTROLLER_SSD1322=3,
       SSDFB_CONTROLLER_SSD1353=4,
} ssdfb_controller_id_t;

typedef enum {
       SSDFB_PRODUCT_UNKNOWN=0,
       SSDFB_PRODUCT_SSD1306_GENERIC=1,
       SSDFB_PRODUCT_SH1106_GENERIC=2,
       SSDFB_PRODUCT_ADAFRUIT_931=3,
       SSDFB_PRODUCT_ADAFRUIT_938=4,
       SSDFB_PRODUCT_SSD1322_GENERIC=5,
       SSDFB_PRODUCT_SSD1353_GENERIC=6,
       SSDFB_PRODUCT_DEP_160128A_RGB=7,
} ssdfb_product_id_t;

#define SSDFB_I2C_DEFAULT_ADDR          0x3c
#define SSDFB_I2C_ALTERNATIVE_ADDR      0x3d

/* Co bit has different behaviour in SSD1306 and SH1106 */
#define SSDFB_I2C_CTRL_BYTE_CONTINUATION_MASK   __BIT(7)
#define SSDFB_I2C_CTRL_BYTE_DATA_MASK           __BIT(6)

union ssdfb_block {
       uint8_t         col[8];
       uint64_t        raw;
};

struct ssdfb_product {
       ssdfb_product_id_t              p_product_id;
       ssdfb_controller_id_t           p_controller_id;
       const char                      *p_name;
       int                             p_width;
       int                             p_height;
       int                             p_bits_per_pixel;
       bool                            p_rgb;
       int                             p_panel_shift;
       uint8_t                         p_fosc;
       uint8_t                         p_fosc_div;
       uint8_t                         p_precharge;
       uint8_t                         p_discharge;
       uint8_t                         p_compin_cfg;
       uint8_t                         p_vcomh_deselect_level;
       uint8_t                         p_default_contrast;
       uint8_t                         p_multiplex_ratio;
       int                             (*p_init)(struct ssdfb_softc *);
       int                             (*p_sync)(struct ssdfb_softc *, bool);
};

struct ssdfb_softc {
       device_t                        sc_dev;
       const struct ssdfb_product      *sc_p;

       /* wscons & rasops state */
       u_int                           sc_mode;
       int                             sc_fontcookie;
       struct wsdisplay_font           *sc_font;
       struct wsscreen_descr           sc_screen_descr;
       const struct wsscreen_descr     *sc_screens[1];
       struct wsscreen_list            sc_screenlist;
       struct rasops_info              sc_ri;
       size_t                          sc_ri_bits_len;
       struct wsdisplay_emulops        sc_orig_riops;
       int                             sc_nscreens;
       device_t                        sc_wsdisplay;
       bool                            sc_is_console;
       bool                            sc_usepoll;

       /* hardware state */
       bool                            sc_upsidedown;
       bool                            sc_inverse;
       uint8_t                         sc_contrast;
       bool                            sc_display_on;
       union ssdfb_block               *sc_gddram;
       size_t                          sc_gddram_len;

       /* damage tracking */
       lwp_t                           *sc_thread;
       kcondvar_t                      sc_cond;
       kmutex_t                        sc_cond_mtx;
       bool                            sc_detaching;
       int                             sc_backoff;
       bool                            sc_modified;
       struct uvm_object               *sc_uobj;

       /* reference to bus-specific code */
       void    *sc_cookie;
       int     (*sc_cmd)(void *, uint8_t *, size_t, bool);
       int     (*sc_transfer_rect)(void *, uint8_t, uint8_t, uint8_t, uint8_t,
                                   uint8_t *, size_t, bool);
};

void    ssdfb_attach(struct ssdfb_softc *, int flags);
int     ssdfb_detach(struct ssdfb_softc *);