use paste::paste;
use tinyvec::ArrayVec;

pub trait Command {
    fn address(&self) -> u8;
    fn args_iter(&'_ self) -> core::slice::Iter<'_, u8>;
}

pub struct ArrayCommand<const N: usize> {
    pub address: u8,
    pub args: ArrayVec<[u8; N]>,
}

impl<const N: usize> Command for ArrayCommand<N> {
    fn address(&self) -> u8 {
        self.address
    }

    fn args_iter(&'_ self) -> core::slice::Iter<'_, u8> {
        self.args.iter()
    }
}

pub struct CustomCommand {
    pub address: u8,
    pub args: &'static [u8],
}

impl CustomCommand {
    pub const fn address_const(&self) -> u8 {
        self.address
    }

    pub const fn args_slice(&self) -> &[u8] {
        self.args
    }
}

impl Command for CustomCommand {
    fn address(&self) -> u8 {
        self.address
    }

    fn args_iter(&'_ self) -> core::slice::Iter<'_, u8> {
        self.args.iter()
    }
}

macro_rules! define_arg {
    (@field ($field:ident: $min:literal..=$max:literal)) => {
        paste! {
            #[allow(unused, clippy::identity_op)]
            pub const fn [<with_ $field>](mut self, val: u8) -> Self {
                const MASK: u8 = ((1 << ($max - $min + 1)) - 1);
                self.0 &= !(MASK << $min); // Clear bits
                self.0 |= (val & MASK) << $min; // Assign bits
                self
            }
        }
    };

    (@field ($field:ident: $index:literal)) => {
        paste! {
            #[allow(unused)]
            pub const fn [<with_ $field>](mut self, val: bool) -> Self {
                self.0 &= !(1 << $index); // Clear bit
                self.0 |= (val as u8) << $index; // Assign bit
                self
            }
        }
    };

    (@field ($field:ident)) => {
        paste! {
            #[allow(unused)]
            pub const fn [<with_ $field>](mut self, val: u8) -> Self {
                self.0 = val;
                self
            }
        }
    };

    ($visibility:vis struct $type:ident {
        const INIT = $init:literal;
        $($field:ident $(: $min:literal$(..=$max:literal)?)?),*$(,)?
    }) => {
        #[derive(Clone, Copy)]
        $visibility struct $type(u8);

        impl Default for $type {
            fn default() -> Self {
                Self::new()
            }
        }

        #[allow(unused)]
        impl $type {
            pub const fn new() -> Self {
                Self($init)
            }

            pub const fn into_byte(self) -> u8 {
                self.0
            }

            $(
                define_arg!(@field ($field $(: $min$(..=$max)?)?));
            )*
        }
    };

    ($visibility:vis struct $type:ident {
        $field:ident $(,)?
    }) => {
        #[derive(Clone, Copy)]
        $visibility struct $type(u8);

        impl Default for $type {
            fn default() -> Self {
                Self::new()
            }
        }

        #[allow(unused)]
        impl $type {
            pub const fn new() -> Self {
                Self(0)
            }

            pub const fn into_byte(self) -> u8 {
                self.0
            }

            define_arg!(@field ($field));
        }
    };
}

macro_rules! define_commands {
    ($(
        $(#[$docs:meta])*
        $visibility:vis command $command:ident {
            const ADDRESS = $address:literal;
            $(
                arg {
                    $($body:tt)*
                }
            )*
        }
    )*) => {
        paste! {
            $(
                $(
                    define_arg!($visibility struct [< Cmd $command Arg ${index()}>] { $($body)* });
                )*

                #[repr(C)]
                #[derive(Default)]
                $(#[$docs])*
                $visibility struct [< Cmd $command >]($(pub [< Cmd $command Arg ${index()} >], $(${ignore($body)})*)*);

                impl [< Cmd $command >] {
                    pub const fn address_const(&self) -> u8 {
                        $address
                    }

                    pub const fn args_slice(&self) -> &[u8] {
                        unsafe {
                            core::slice::from_raw_parts(
                                self as *const [< Cmd $command >] as *const u8,
                                0 $(+ 1 $(${ignore($body)})*)*,
                            )
                        }
                    }
                }

                impl Command for [< Cmd $command >] {
                    fn address(&self) -> u8 {
                        $address
                    }

                    fn args_iter(&'_ self) -> core::slice::Iter<'_, u8> {
                        self.into_iter()
                    }
                }

                impl<'a> IntoIterator for &'a [< Cmd $command >] {
                    type Item = &'a u8;
                    type IntoIter = core::slice::Iter<'a, u8>;

                    fn into_iter(self) -> Self::IntoIter {
                        self.args_slice().iter()
                    }
                }
            )*
        }
    };
}

define_commands! {
    // System function command table 1

    #[doc="No operation"]
    pub command Nop {
        const ADDRESS = 0x00;
    }

    #[doc="Software reset"]
    pub command Swreset {
        const ADDRESS = 0x01;
    }

    #[doc="ID read"]
    pub command Rddid {
        const ADDRESS = 0x04;
        arg { id1 }
        arg { id2 }
        arg { id3 }
    }

    #[doc="ID read"]
    pub command Rdnumed {
        const ADDRESS = 0x05;
        arg {
            const INIT = 0b0000_0000;
            err: 0..=6,
            err_over: 7,
        }
    }

    #[doc="Read the first pixel of color R"]
    pub command Rdred {
        const ADDRESS = 0x06;
        arg { r_1st }
    }

    #[doc="Read the first pixel of color G"]
    pub command Rdgreen {
        const ADDRESS = 0x07;
        arg { g_1st }
    }

    #[doc="Read the first pixel of color B"]
    pub command Rdblue {
        const ADDRESS = 0x08;
        arg { b_1st }
    }

    #[doc="Read display power mode"]
    pub command Rddpm {
        const ADDRESS = 0x0A;
        arg {
            const INIT = 0b0000_1000;
            dison: 2,
            slpout: 4,
            bston: 7,
        }
    }

    #[doc="Read display MADCTR"]
    pub command Rddmadctl {
        const ADDRESS = 0x0B;
        arg {
            const INIT = 0b0000_1000;
            bgr: 3,
            ml: 4,
        }
    }

    #[doc="Read display pixel format"]
    pub command Rddcolmod {
        const ADDRESS = 0x0C;
        arg {
            const INIT = 0b0000_0000;
            vipf: 5..=6,
        }
    }

    #[doc="Read display image mode"]
    pub command Rddim {
        const ADDRESS = 0x0D;
        arg {
            const INIT = 0b0000_0000;
            gcs: 0..=2,
            alpxloff: 3,
            alpxlon: 4,
            invon: 5,
        }
    }

    #[doc="Read display signal mode"]
    pub command Rddsm {
        const ADDRESS = 0x0E;
        arg {
            const INIT = 0b0000_0000;
            telmd: 6,
            teon: 7,
        }
    }

    #[doc="Read display self-diagnostic result"]
    pub command Rddsdr {
        const ADDRESS = 0x0F;
        arg {
            const INIT = 0b0000_0000;
            fund: 6,
            rld: 7,
        }
    }

    #[doc="Sleep in"]
    pub command Slpin {
        const ADDRESS = 0x10;
    }

    #[doc="Sleep out"]
    pub command Slpout {
        const ADDRESS = 0x11;
    }

    #[doc="Partial mode on"]
    pub command Ptlon {
        const ADDRESS = 0x12;
    }

    #[doc="Normal display mode on"]
    pub command Noron {
        const ADDRESS = 0x13;
    }

    #[doc="Display inversion off (normal)"]
    pub command Invoff {
        const ADDRESS = 0x20;
    }

    #[doc="Display inversion on"]
    pub command Invon {
        const ADDRESS = 0x21;
    }

    #[doc="All pixel off (black)"]
    pub command Allpoff {
        const ADDRESS = 0x22;
    }

    #[doc="All pixel on (white)"]
    pub command Allpon {
        const ADDRESS = 0x23;
    }

    #[doc="Gamma curve select"]
    pub command Gamset {
        const ADDRESS = 0x26;
        arg {
            const INIT = 0b0000_0000;
            gc: 0..=3,
        }
    }

    #[doc="Display off"]
    pub command Dispoff {
        const ADDRESS = 0x28;
    }

    #[doc="Display on"]
    pub command Dispon {
        const ADDRESS = 0x29;
    }

    #[doc="Tearing effect line off"]
    pub command Teoff {
        const ADDRESS = 0x34;
    }

    #[doc="Tearing effect line on"]
    pub command Teon {
        const ADDRESS = 0x35;
    }

    #[doc="Display data access control"]
    pub command Madctl {
        const ADDRESS = 0x36;
        arg {
            const INIT = 0b0000_0000;
            bgr: 3,
            ml: 4,
        }
    }

    #[doc="Idle mode off"]
    pub command Idmoff {
        const ADDRESS = 0x38;
    }

    #[doc="Idle mode on"]
    pub command Idmon {
        const ADDRESS = 0x39;
    }

    #[doc="Interface pixel format"]
    pub command Colmod {
        const ADDRESS = 0x3A;
        arg {
            const INIT = 0b0000_0000;
            vipf: 4..=6,
        }
    }

    #[doc="Read tear line"]
    pub command Gsl {
        const ADDRESS = 0x45;
        arg { tesl_msb }
        arg { tesl_lsb }
    }

    #[doc="Write display brightness value"]
    pub command Wrdibv {
        const ADDRESS = 0x51;
        arg { dbv }
    }

    #[doc="Read display brightness value"]
    pub command Rddisbv {
        const ADDRESS = 0x52;
        arg { dbv }
    }

    #[doc="Write control display value"]
    pub command Wrctrld {
        const ADDRESS = 0x53;
        arg {
            const INIT = 0b0000_0000;
            bl: 2,
            dd: 3,
            bctrl: 5,
        }
    }

    #[doc="Read control display value"]
    pub command Rrctrld {
        const ADDRESS = 0x54;
        arg {
            const INIT = 0b0000_0000;
            bl: 2,
            dd: 3,
            bctrl: 5,
        }
    }

    #[doc="Write CABC mode"]
    pub command Wrcabc {
        const ADDRESS = 0x55;
        arg {
            const INIT = 0b0000_0000;
            cabc_md: 0..=1,
            ce_md: 4..=5,
            ce_on: 7,
        }
    }

    #[doc="Read CABC mode"]
    pub command Rrcabc {
        const ADDRESS = 0x56;
        arg {
            const INIT = 0b0000_0000;
            cabc_md: 0..=1,
            ce_md: 4..=5,
            ce_on: 7,
        }
    }

    #[doc="Write CABC minimum brightness"]
    pub command Wrcabcmb {
        const ADDRESS = 0x5E;
        arg { cmb }
    }

    #[doc="Read CABC minimum brightness"]
    pub command Rrcabcmb {
        const ADDRESS = 0x5F;
        arg { cmb }
    }

    #[doc="Read automatic brightness control self-diagnostic result"]
    pub command Rdabcsd {
        const ADDRESS = 0x68;
        arg {
            const INIT = 0b0000_0000;
            fund: 6,
            rld: 7,
        }
    }

    #[doc="Read black/white low bits"]
    pub command Rdbwlb {
        const ADDRESS = 0x70;
        arg {
            const INIT = 0b0000_0000;
            wy: 0..=1,
            wx: 2..=3,
            bky: 4..=5,
            bkx: 6..=7,
        }
    }

    #[doc="Read Bkx"]
    pub command Rdbkx {
        const ADDRESS = 0x71;
        arg { bkx }
    }

    #[doc="Read Bky"]
    pub command Rdbky {
        const ADDRESS = 0x72;
        arg { bky }
    }

    #[doc="Read Wx"]
    pub command Rdwx {
        const ADDRESS = 0x73;
        arg { wx }
    }

    #[doc="Read Wy"]
    pub command Rdwy {
        const ADDRESS = 0x74;
        arg { wy }
    }

    #[doc="Read red/green low bits"]
    pub command Rdrglb {
        const ADDRESS = 0x75;
        arg {
            const INIT = 0b0000_0000;
            gy: 0..=1,
            gx: 2..=3,
            ry: 4..=5,
            rx: 6..=7,
        }
    }

    #[doc="Read Rx"]
    pub command Rdrx {
        const ADDRESS = 0x76;
        arg { rx }
    }

    #[doc="Read Ry"]
    pub command Rdry {
        const ADDRESS = 0x77;
        arg { ry }
    }

    #[doc="Read Gx"]
    pub command Rdgx {
        const ADDRESS = 0x78;
        arg { gx }
    }

    #[doc="Read Gy"]
    pub command Rdgy {
        const ADDRESS = 0x79;
        arg { gy }
    }

    #[doc="Read blue/a-color low bits"]
    pub command Rdbalb {
        const ADDRESS = 0x7A;
        arg {
            const INIT = 0b0000_0000;
            ay: 0..=1,
            ax: 2..=3,
            by: 4..=5,
            bx: 6..=7,
        }
    }

    #[doc="Read Bx"]
    pub command Rdbx {
        const ADDRESS = 0x7B;
        arg { bx }
    }

    #[doc="Read By"]
    pub command Rdby {
        const ADDRESS = 0x7C;
        arg { by }
    }

    #[doc="Read Ax"]
    pub command Rdax {
        const ADDRESS = 0x7D;
        arg { ax }
    }

    #[doc="Read Ay"]
    pub command Rday {
        const ADDRESS = 0x7E;
        arg { ay }
    }

    #[doc="Read tthe DDB from the provided location"]
    pub command Rdddbs {
        const ADDRESS = 0xA1;
        arg { const INIT = 0x88; }
        arg { const INIT = 0x02; }
        arg { mid_msb }
        arg { mid_lsb }
        arg { const INIT = 0xFF; }
    }

    #[doc="Continue reading the DDB from the last read location"]
    pub command Rdddbc {
        const ADDRESS = 0xA8;
        arg { sid_msb }
        arg { sid_lsb }
        arg { mid_msb }
        arg { mid_lsb }
        arg { const INIT = 0xFF; }
    }

    #[doc="Read first checksum"]
    pub command Rdfcs {
        const ADDRESS = 0xAA;
        arg { fcs }
    }

    #[doc="Read continue checksum"]
    pub command Rdccs {
        const ADDRESS = 0xAF;
        arg { ccs }
    }

    #[doc="Read ID1"]
    pub command Rdid1 {
        const ADDRESS = 0xDA;
        arg { id1 }
    }

    #[doc="Read ID2"]
    pub command Rdid2 {
        const ADDRESS = 0xDB;
        arg { id2 }
    }

    #[doc="Read ID3"]
    pub command Rdid3 {
        const ADDRESS = 0xDC;
        arg { id3 }
    }

    // System function command table 2

    #[doc="Command2_BKx function selection"]
    pub command Cn2bkxsel {
        const ADDRESS = 0xFF;
        arg { const INIT = 0b0111_0111; }
        arg { const INIT = 0b0000_0001; }
        arg { const INIT = 0b0000_0000; }
        arg { const INIT = 0b0000_0000; }
        arg {
            const INIT = 0b0000_0000;
            // The spec is ambiguous on the size of this field.
            bksel: 0..=1,
            cn2: 4,
        }
    }

    // Command2_BK0

    #[doc="Positive voltage gamma control"]
    pub command Pvgamctrl {
        const ADDRESS = 0xB0;
        arg {
            const INIT = 0b0000_0000;
            vc0p: 0..=3,
            aj0p: 6..=7,
        }
        arg {
            const INIT = 0b0000_0000;
            vc4p: 0..=5,
            aj1p: 6..=7,
        }
        arg {
            const INIT = 0b0000_0000;
            vc8p: 0..=5,
            aj2p: 6..=7,
        }
        arg {
            const INIT = 0b0000_0000;
            vc16p: 0..=4,
        }
        arg {
            const INIT = 0b0000_0000;
            vc24p: 0..=4,
            aj3p: 6..=7,
        }
        arg {
            const INIT = 0b0000_0000;
            vc52p: 0..=3,
        }
        arg {
            const INIT = 0b0000_0000;
            vc80p: 0..=5,
        }
        arg {
            const INIT = 0b0000_0000;
            vc108p: 0..=3,
        }
        arg {
            const INIT = 0b0000_0000;
            vc147p: 0..=3,
        }
        arg {
            const INIT = 0b0000_0000;
            vc175p: 0..=5,
        }
        arg {
            const INIT = 0b0000_0000;
            vc203p: 0..=3,
        }
        arg {
            const INIT = 0b0000_0000;
            vc231p: 0..=4,
            aj4p: 6..=7,
        }
        arg {
            const INIT = 0b0000_0000;
            vc239p: 0..=4,
        }
        arg {
            const INIT = 0b0000_0000;
            vc247p: 0..=5,
            aj5p: 6..=7,
        }
        arg {
            const INIT = 0b0000_0000;
            vc251p: 0..=5,
            aj6p: 6..=7,
        }
        arg {
            const INIT = 0b0000_0000;
            vc255p: 0..=4,
            aj7p: 6..=7,
        }
    }

    #[doc="Negative voltage gamma control"]
    pub command Nvgamctrl {
        const ADDRESS = 0xB1;
        arg {
            const INIT = 0b0000_0000;
            vc0n: 0..=3,
            aj0n: 6..=7,
        }
        arg {
            const INIT = 0b0000_0000;
            vc4n: 0..=5,
            aj1n: 6..=7,
        }
        arg {
            const INIT = 0b0000_0000;
            vc8n: 0..=5,
            aj2n: 6..=7,
        }
        arg {
            const INIT = 0b0000_0000;
            vc16n: 0..=4,
        }
        arg {
            const INIT = 0b0000_0000;
            vc24n: 0..=4,
            // TODO: This field is not documented in the spec,
            // but it seems like it should be here?
            aj3n: 6..=7,
        }
        arg {
            const INIT = 0b0000_0000;
            vc52n: 0..=3,
        }
        arg {
            const INIT = 0b0000_0000;
            vc80n: 0..=5,
        }
        arg {
            const INIT = 0b0000_0000;
            vc108n: 0..=3,
        }
        arg {
            const INIT = 0b0000_0000;
            vc147n: 0..=3,
        }
        arg {
            const INIT = 0b0000_0000;
            vc175n: 0..=5,
        }
        arg {
            const INIT = 0b0000_0000;
            vc203n: 0..=3,
        }
        arg {
            const INIT = 0b0000_0000;
            vc231n: 0..=4,
            aj4n: 6..=7,
        }
        arg {
            const INIT = 0b0000_0000;
            vc239n: 0..=4,
        }
        arg {
            const INIT = 0b0000_0000;
            vc247n: 0..=5,
            aj5n: 6..=7,
        }
        arg {
            const INIT = 0b0000_0000;
            vc251n: 0..=5,
            aj6n: 6..=7,
        }
        arg {
            const INIT = 0b0000_0000;
            vc255n: 0..=4,
            aj7n: 6..=7,
        }
    }

    #[doc="Digital gamma enable"]
    pub command Dgmen {
        const ADDRESS = 0xB8;
        arg {
            const INIT = 0b0000_0000;
            dgm_on: 4,
        }
    }

    // DGMLUTR, DGMLUTB not currently supported, because of its high number of parameters.
    // It could be supported with a manual implementation of `Command`.

    #[doc="PWM CLK select"]
    pub command Pwmclk {
        const ADDRESS = 0xBC;
        arg {
            const INIT = 0b0001_1000;
            pwm_clk_sel: 0..=2,
        }
    }

    #[doc="Display line setting"]
    pub command Lneset {
        const ADDRESS = 0xC0;
        arg {
            const INIT = 0b0000_0000;
            bar: 0..=6,
            lde_en: 7,
        }
        arg {
            const INIT = 0b0000_0000;
            line_delta: 0..=1,
        }
    }

    #[doc="Porch control"]
    pub command Porctrl {
        const ADDRESS = 0xC1;
        arg { vbp }
        arg { vfp }
    }

    #[doc="Inversion selection & frame rate control"]
    pub command Invsel {
        const ADDRESS = 0xC2;
        arg {
            const INIT = 0b0011_0000;
            nlinv: 0..=2,
        }
        arg {
            const INIT = 0b0000_0000;
            rtni: 0..=4,
        }
    }

    #[doc="RGB control"]
    pub command Rgbctrl {
        const ADDRESS = 0xC3;
        arg {
            const INIT = 0b0000_0000;
            ep: 0,
            dp: 1,
            hsp: 2,
            vsp: 3,
            de_hv: 7,
        }
        arg { hbp_hvrgb }
        arg { vbp_hvrgb }
    }

    #[doc="Partial mode control"]
    pub command Parctrl {
        const ADDRESS = 0xC5;
        arg { ptsa_lsb }
        arg {
            const INIT = 0b0000_0000;
            ptsa_hsb: 0..=1,
        }
        arg { ptea_lsb }
        arg {
            const INIT = 0b0000_0000;
            ptea_hsb: 0..=1,
        }
    }

    #[doc="Source direction control"]
    pub command Sdir {
        const ADDRESS = 0xC7;
        arg {
            const INIT = 0b0000_0000;
            ss: 2,
        }
    }

    #[doc="Pseudo-dot inversion driving setting"]
    pub command Pdotset {
        const ADDRESS = 0xC8;
        arg {
            const INIT = 0b0000_0000;
            z_gltor: 5,
            z_sdm1s: 6,
            z_en: 7,
        }
    }

    #[doc="Color control"]
    pub command Colctrl {
        const ADDRESS = 0xCD;
        arg {
            const INIT = 0b0000_0000;
            epf: 0..=2,
            mdt: 3,
            inv_led_on: 4,
            inv_led_pwm: 5,
        }
    }

    #[doc="Spread spectrum control"]
    pub command Ssctrl {
        const ADDRESS = 0xCE;
        arg {
            const INIT = 0b0000_0100;
            dssrg: 4..=5,
            dsse: 7,
        }
    }

    #[doc="Sunlight readable enhancement"]
    pub command Sectrl {
        const ADDRESS = 0xE0;
        arg {
            const INIT = 0b0000_0000;
            sre_alpha: 0..=3,
            sre: 4,
        }
    }

    #[doc="Noise reduce control"]
    pub command Nrctrl {
        const ADDRESS = 0xE1;
        arg {
            const INIT = 0b0000_0000;
            nr_md: 0..=1,
            nre: 4,
        }
    }

    #[doc="Sharpness control (originally has a conflicting name SECTRL)"]
    pub command Shctrl {
        const ADDRESS = 0xE2;
        arg {
            const INIT = 0b0000_0000;
            y_gain: 0..=3,
            se: 4,
        }
    }

    #[doc="Color calibration control"]
    pub command Ccctrl {
        const ADDRESS = 0xE3;
        arg {
            const INIT = 0b0000_0000;
            cce: 0,
        }
    }

    #[doc="Skin tone preservation control"]
    pub command Skctrl {
        const ADDRESS = 0xE4;
        arg {
            const INIT = 0b0000_0000;
            skin_ce_mid: 0..=1,
            ske: 4,
        }
    }

    #[doc="NVM address setting enable"]
    pub command Nvmsete {
        const ADDRESS = 0xEA;
        arg {
            const INIT = 0b0000_0000;
            aden: 0,
        }
    }

    #[doc="CABC control"]
    pub command Cabcctrl {
        const ADDRESS = 0xEE;
        arg {
            const INIT = 0b0000_0000;
            led_en: 0,
            ledpwr_sel: 4,
        }
    }

    // Command2_BK1

    #[doc="Vop amplitude setting"]
    pub command Vrhs {
        const ADDRESS = 0xB0;
        arg { vrha }
    }

    #[doc="VCOM amplitude setting"]
    pub command Vcoms {
        const ADDRESS = 0xB1;
        arg { vcom }
    }

    #[doc="VGH voltage setting"]
    pub command Vghss {
        const ADDRESS = 0xB2;
        arg {
            const INIT = 0b0000_0000;
            vghss: 0..=3,
        }
    }

    #[doc="TEST command setting"]
    pub command Tescmd {
        const ADDRESS = 0xB3;
        arg { const INIT = 0b1000_0000; }
    }

    #[doc="VGL voltage setting"]
    pub command Vgls {
        const ADDRESS = 0xB5;
        arg {
            const INIT = 0b0100_0000;
            vgls: 0..=3,
        }
    }

    #[doc="VRH_DV voltage setting"]
    pub command Vrhdv {
        const ADDRESS = 0xB6;
        arg {
            const INIT = 0b0000_0000;
            vrh_dv: 0..=6,
        }
    }

    #[doc="Power control 1"]
    pub command Pwctrl1 {
        const ADDRESS = 0xB7;
        arg {
            const INIT = 0b0000_0000;
            apos: 0..=1,
            apis: 2..=3,
            ap: 6..=7,
        }
    }

    #[doc="Power control 2"]
    pub command Pwctrl2 {
        const ADDRESS = 0xB8;
        arg {
            const INIT = 0b0000_0000;
            avcl: 0..=1,
            avdd: 4..=5,
        }
    }

    #[doc="Power control 3"]
    pub command Pwctrl3 {
        const ADDRESS = 0xB9;
        arg {
            const INIT = 0b0000_0000;
            svno_pum: 0..=1,
            svpo_pum: 4..=5,
        }
    }

    #[doc="Power pumping clk selection 1"]
    pub command Pclks1 {
        const ADDRESS = 0xBA;
        arg {
            const INIT = 0b0000_0000;
            stp1cks: 0..=1,
            stp4cks: 4..=5,
        }
    }

    #[doc="Power pumping clk selection 2"]
    pub command Pclks2 {
        const ADDRESS = 0xBB;
        arg {
            const INIT = 0b0000_0000;
            sbstcks: 0..=1,
        }
    }

    #[doc="Power pumping clk selection 3"]
    pub command Pclks3 {
        const ADDRESS = 0xBC;
        arg {
            const INIT = 0b0000_0000;
            stp2scks: 0..=1,
            stp2pcks: 0..=1,
            stp3cks: 4..=5,
        }
    }

    #[doc="Source pre_drive timing set1 (also known as SPD1)"]
    pub command Pdr1 {
        const ADDRESS = 0xC1;
        arg {
            const INIT = 0b0111_0000;
            t2d: 0..=3,
        }
    }

    #[doc="Source pre_drive timing set2 (also known as SPD2)"]
    pub command Pdr2 {
        const ADDRESS = 0xC2;
        arg {
            const INIT = 0b0111_0000;
            t3d: 0..=3,
        }
    }

    #[doc="MIPI setting 1"]
    pub command Mipiset1 {
        const ADDRESS = 0xD0;
        arg {
            const INIT = 0b1000_0000;
            err_sel: 0..=1,
            eotp_en: 3,
        }
    }

    #[doc="MIPI setting 2"]
    pub command Mipiset2 {
        const ADDRESS = 0xD1;
        arg {
            const INIT = 0b0000_0000;
            mpc_tlpx0: 0..=3,
            mpc_tlpx1: 4..=7,
        }
        arg {
            const INIT = 0b0000_0000;
            mpc_tlpx2: 0..=3,
            mpc_txtimeadj: 4..=7,
        }
        arg {
            const INIT = 0b0000_0000;
            mpc_ttago: 0..=3,
        }
        arg {
            const INIT = 0b0000_0000;
            mpc_ttaget: 0..=3,
        }
    }

    #[doc="MIPI setting 3"]
    pub command Mipiset3 {
        const ADDRESS = 0xD2;
        arg {
            const INIT = 0b0011_0000;
            phy_ttasure: 0..=3,
        }
    }

    #[doc="MIPI setting 4"]
    pub command Mipiset4 {
        const ADDRESS = 0xD3;
        arg {
            const INIT = 0b0000_0000;
            phy_csk: 0..=2,
        }
        arg {
            const INIT = 0b0000_0000;
            phy_dsk0: 0..=2,
            phy_dsk1: 4..=6,
        }
    }

    // Command2_BK3

    #[doc="NVM enable"]
    pub command Nvmen {
        const ADDRESS = 0xC8;
        arg { const INIT = 0b0111_0111; }
        arg { const INIT = 0b0000_0001; }
        arg { const INIT = 0b1110_1110; }
        arg { const INIT = 0b0000_0100; }
    }

    #[doc="NVM manual control setting"]
    pub command Nvmset {
        const ADDRESS = 0xCA;
        arg {
            const INIT = 0b0000_0000;
            pa_msb: 0..=1,
        }
        arg { pa_lsb }
        arg { pdin }
    }

    #[doc="NVM program active"]
    pub command Promact {
        const ADDRESS = 0xCC;
        arg { const INIT = 0b1010_1010; }
    }
}
