Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.19-rc4 251 lines 5.7 kB view raw
1#include <linux/amba/bus.h> 2#include <linux/amba/clcd.h> 3#include <linux/gpio/consumer.h> 4#include <linux/of.h> 5#include <linux/of_graph.h> 6#include <linux/delay.h> 7#include <linux/bitops.h> 8#include <linux/mfd/syscon.h> 9#include <linux/regmap.h> 10 11#include "amba-clcd-nomadik.h" 12 13static struct gpio_desc *grestb; 14static struct gpio_desc *scen; 15static struct gpio_desc *scl; 16static struct gpio_desc *sda; 17 18static u8 tpg110_readwrite_reg(bool write, u8 address, u8 outval) 19{ 20 int i; 21 u8 inval = 0; 22 23 /* Assert SCEN */ 24 gpiod_set_value_cansleep(scen, 1); 25 ndelay(150); 26 /* Hammer out the address */ 27 for (i = 5; i >= 0; i--) { 28 if (address & BIT(i)) 29 gpiod_set_value_cansleep(sda, 1); 30 else 31 gpiod_set_value_cansleep(sda, 0); 32 ndelay(150); 33 /* Send an SCL pulse */ 34 gpiod_set_value_cansleep(scl, 1); 35 ndelay(160); 36 gpiod_set_value_cansleep(scl, 0); 37 ndelay(160); 38 } 39 40 if (write) { 41 /* WRITE */ 42 gpiod_set_value_cansleep(sda, 0); 43 } else { 44 /* READ */ 45 gpiod_set_value_cansleep(sda, 1); 46 } 47 ndelay(150); 48 /* Send an SCL pulse */ 49 gpiod_set_value_cansleep(scl, 1); 50 ndelay(160); 51 gpiod_set_value_cansleep(scl, 0); 52 ndelay(160); 53 54 if (!write) 55 /* HiZ turn-around cycle */ 56 gpiod_direction_input(sda); 57 ndelay(150); 58 /* Send an SCL pulse */ 59 gpiod_set_value_cansleep(scl, 1); 60 ndelay(160); 61 gpiod_set_value_cansleep(scl, 0); 62 ndelay(160); 63 64 /* Hammer in/out the data */ 65 for (i = 7; i >= 0; i--) { 66 int value; 67 68 if (write) { 69 value = !!(outval & BIT(i)); 70 gpiod_set_value_cansleep(sda, value); 71 } else { 72 value = gpiod_get_value(sda); 73 if (value) 74 inval |= BIT(i); 75 } 76 ndelay(150); 77 /* Send an SCL pulse */ 78 gpiod_set_value_cansleep(scl, 1); 79 ndelay(160); 80 gpiod_set_value_cansleep(scl, 0); 81 ndelay(160); 82 } 83 84 gpiod_direction_output(sda, 0); 85 /* Deassert SCEN */ 86 gpiod_set_value_cansleep(scen, 0); 87 /* Satisfies SCEN pulse width */ 88 udelay(1); 89 90 return inval; 91} 92 93static u8 tpg110_read_reg(u8 address) 94{ 95 return tpg110_readwrite_reg(false, address, 0); 96} 97 98static void tpg110_write_reg(u8 address, u8 outval) 99{ 100 tpg110_readwrite_reg(true, address, outval); 101} 102 103static void tpg110_startup(struct device *dev) 104{ 105 u8 val; 106 107 dev_info(dev, "TPG110 display enable\n"); 108 /* De-assert the reset signal */ 109 gpiod_set_value_cansleep(grestb, 0); 110 mdelay(1); 111 dev_info(dev, "de-asserted GRESTB\n"); 112 113 /* Test display communication */ 114 tpg110_write_reg(0x00, 0x55); 115 val = tpg110_read_reg(0x00); 116 if (val == 0x55) 117 dev_info(dev, "passed communication test\n"); 118 val = tpg110_read_reg(0x01); 119 dev_info(dev, "TPG110 chip ID: %d version: %d\n", 120 val>>4, val&0x0f); 121 122 /* Show display resolution */ 123 val = tpg110_read_reg(0x02); 124 val &= 7; 125 switch (val) { 126 case 0x0: 127 dev_info(dev, "IN 400x240 RGB -> OUT 800x480 RGB (dual scan)"); 128 break; 129 case 0x1: 130 dev_info(dev, "IN 480x272 RGB -> OUT 800x480 RGB (dual scan)"); 131 break; 132 case 0x4: 133 dev_info(dev, "480x640 RGB"); 134 break; 135 case 0x5: 136 dev_info(dev, "480x272 RGB"); 137 break; 138 case 0x6: 139 dev_info(dev, "640x480 RGB"); 140 break; 141 case 0x7: 142 dev_info(dev, "800x480 RGB"); 143 break; 144 default: 145 dev_info(dev, "ILLEGAL RESOLUTION"); 146 break; 147 } 148 149 val = tpg110_read_reg(0x03); 150 dev_info(dev, "resolution is controlled by %s\n", 151 (val & BIT(7)) ? "software" : "hardware"); 152} 153 154static void tpg110_enable(struct clcd_fb *fb) 155{ 156 struct device *dev = &fb->dev->dev; 157 static bool startup; 158 u8 val; 159 160 if (!startup) { 161 tpg110_startup(dev); 162 startup = true; 163 } 164 165 /* Take chip out of standby */ 166 val = tpg110_read_reg(0x03); 167 val |= BIT(0); 168 tpg110_write_reg(0x03, val); 169} 170 171static void tpg110_disable(struct clcd_fb *fb) 172{ 173 u8 val; 174 175 dev_info(&fb->dev->dev, "TPG110 display disable\n"); 176 val = tpg110_read_reg(0x03); 177 /* Put into standby */ 178 val &= ~BIT(0); 179 tpg110_write_reg(0x03, val); 180} 181 182static void tpg110_init(struct device *dev, struct device_node *np, 183 struct clcd_board *board) 184{ 185 dev_info(dev, "TPG110 display init\n"); 186 187 /* This asserts the GRESTB signal, putting the display into reset */ 188 grestb = devm_fwnode_get_gpiod_from_child(dev, "grestb", &np->fwnode, 189 GPIOD_OUT_HIGH, "grestb"); 190 if (IS_ERR(grestb)) { 191 dev_err(dev, "no GRESTB GPIO\n"); 192 return; 193 } 194 scen = devm_fwnode_get_gpiod_from_child(dev, "scen", &np->fwnode, 195 GPIOD_OUT_LOW, "scen"); 196 if (IS_ERR(scen)) { 197 dev_err(dev, "no SCEN GPIO\n"); 198 return; 199 } 200 scl = devm_fwnode_get_gpiod_from_child(dev, "scl", &np->fwnode, 201 GPIOD_OUT_LOW, "scl"); 202 if (IS_ERR(scl)) { 203 dev_err(dev, "no SCL GPIO\n"); 204 return; 205 } 206 sda = devm_fwnode_get_gpiod_from_child(dev, "sda", &np->fwnode, 207 GPIOD_OUT_LOW, "sda"); 208 if (IS_ERR(sda)) { 209 dev_err(dev, "no SDA GPIO\n"); 210 return; 211 } 212 board->enable = tpg110_enable; 213 board->disable = tpg110_disable; 214} 215 216int nomadik_clcd_init_panel(struct clcd_fb *fb, struct device_node *panel) 217{ 218 if (of_device_is_compatible(panel, "tpo,tpg110")) 219 tpg110_init(&fb->dev->dev, panel, fb->board); 220 else 221 dev_info(&fb->dev->dev, "unknown panel\n"); 222 223 /* Unknown panel, fall through */ 224 return 0; 225} 226EXPORT_SYMBOL_GPL(nomadik_clcd_init_panel); 227 228#define PMU_CTRL_OFFSET 0x0000 229#define PMU_CTRL_LCDNDIF BIT(26) 230 231int nomadik_clcd_init_board(struct amba_device *adev, 232 struct clcd_board *board) 233{ 234 struct regmap *pmu_regmap; 235 236 dev_info(&adev->dev, "Nomadik CLCD board init\n"); 237 pmu_regmap = 238 syscon_regmap_lookup_by_compatible("stericsson,nomadik-pmu"); 239 if (IS_ERR(pmu_regmap)) { 240 dev_err(&adev->dev, "could not find PMU syscon regmap\n"); 241 return PTR_ERR(pmu_regmap); 242 } 243 regmap_update_bits(pmu_regmap, 244 PMU_CTRL_OFFSET, 245 PMU_CTRL_LCDNDIF, 246 0); 247 dev_info(&adev->dev, "set PMU mux to CLCD mode\n"); 248 249 return 0; 250} 251EXPORT_SYMBOL_GPL(nomadik_clcd_init_board);