ihs_video_out.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * (C) Copyright 2017
  4. * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
  5. *
  6. * based on the gdsys osd driver, which is
  7. *
  8. * (C) Copyright 2010
  9. * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.de
  10. */
  11. #include <common.h>
  12. #include <display.h>
  13. #include <dm.h>
  14. #include <log.h>
  15. #include <regmap.h>
  16. #include <video_osd.h>
  17. #include <asm/gpio.h>
  18. static const uint MAX_X_CHARS = 53;
  19. static const uint MAX_Y_CHARS = 26;
  20. static const uint MAX_VIDEOMEM_WIDTH = 64;
  21. static const uint MAX_VIDEOMEM_HEIGHT = 32;
  22. static const uint CHAR_WIDTH = 12;
  23. static const uint CHAR_HEIGHT = 18;
  24. static const u16 BASE_WIDTH_MASK = 0x3f00;
  25. static const uint BASE_WIDTH_SHIFT = 8;
  26. static const u16 BASE_HEIGTH_MASK = 0x001f;
  27. static const uint BASE_HEIGTH_SHIFT;
  28. struct ihs_video_out_regs {
  29. /* Device version register */
  30. u16 versions;
  31. /* Device feature register */
  32. u16 features;
  33. /* Device control register */
  34. u16 control;
  35. /* Register controlling screen size */
  36. u16 xy_size;
  37. /* Register controlling screen scaling */
  38. u16 xy_scale;
  39. /* Register controlling screen x position */
  40. u16 x_pos;
  41. /* Register controlling screen y position */
  42. u16 y_pos;
  43. };
  44. #define ihs_video_out_set(map, member, val) \
  45. regmap_range_set(map, 1, struct ihs_video_out_regs, member, val)
  46. #define ihs_video_out_get(map, member, valp) \
  47. regmap_range_get(map, 1, struct ihs_video_out_regs, member, valp)
  48. enum {
  49. CONTROL_FILTER_BLACK = (0 << 0),
  50. CONTROL_FILTER_ORIGINAL = (1 << 0),
  51. CONTROL_FILTER_DARKER = (2 << 0),
  52. CONTROL_FILTER_GRAY = (3 << 0),
  53. CONTROL_MODE_PASSTHROUGH = (0 << 3),
  54. CONTROL_MODE_OSD = (1 << 3),
  55. CONTROL_MODE_AUTO = (2 << 3),
  56. CONTROL_MODE_OFF = (3 << 3),
  57. CONTROL_ENABLE_OFF = (0 << 6),
  58. CONTROL_ENABLE_ON = (1 << 6),
  59. };
  60. struct ihs_video_out_priv {
  61. /* Register map for OSD device */
  62. struct regmap *map;
  63. /* Pointer to video memory */
  64. u16 *vidmem;
  65. /* Display width in text columns */
  66. uint base_width;
  67. /* Display height in text rows */
  68. uint base_height;
  69. /* x-resolution of the display in pixels */
  70. uint res_x;
  71. /* y-resolution of the display in pixels */
  72. uint res_y;
  73. /* OSD's sync mode (resolution + frequency) */
  74. int sync_src;
  75. /* The display port output for this OSD */
  76. struct udevice *video_tx;
  77. /* The pixel clock generator for the display */
  78. struct udevice *clk_gen;
  79. };
  80. static const struct udevice_id ihs_video_out_ids[] = {
  81. { .compatible = "gdsys,ihs_video_out" },
  82. { }
  83. };
  84. /**
  85. * set_control() - Set the control register to a given value
  86. *
  87. * The current value of sync_src is preserved by the function automatically.
  88. *
  89. * @dev: the OSD device whose control register to set
  90. * @value: the 16-bit value to write to the control register
  91. * Return: 0
  92. */
  93. static int set_control(struct udevice *dev, u16 value)
  94. {
  95. struct ihs_video_out_priv *priv = dev_get_priv(dev);
  96. if (priv->sync_src)
  97. value |= ((priv->sync_src & 0x7) << 8);
  98. ihs_video_out_set(priv->map, control, value);
  99. return 0;
  100. }
  101. int ihs_video_out_get_info(struct udevice *dev, struct video_osd_info *info)
  102. {
  103. struct ihs_video_out_priv *priv = dev_get_priv(dev);
  104. u16 versions;
  105. ihs_video_out_get(priv->map, versions, &versions);
  106. info->width = priv->base_width;
  107. info->height = priv->base_height;
  108. info->major_version = versions / 100;
  109. info->minor_version = versions % 100;
  110. return 0;
  111. }
  112. int ihs_video_out_set_mem(struct udevice *dev, uint col, uint row, u8 *buf,
  113. size_t buflen, uint count)
  114. {
  115. struct ihs_video_out_priv *priv = dev_get_priv(dev);
  116. int res;
  117. uint offset;
  118. uint k, rep;
  119. u16 data;
  120. /* Repetitions (controlled via count parmeter) */
  121. for (rep = 0; rep < count; ++rep) {
  122. offset = row * priv->base_width + col + rep * (buflen / 2);
  123. /* Write a single buffer copy */
  124. for (k = 0; k < buflen / 2; ++k) {
  125. uint max_size = priv->base_width * priv->base_height;
  126. if (offset + k >= max_size) {
  127. debug("%s: Write would be out of OSD bounds\n",
  128. dev->name);
  129. return -E2BIG;
  130. }
  131. data = buf[2 * k + 1] + 256 * buf[2 * k];
  132. out_le16(priv->vidmem + offset + k, data);
  133. }
  134. }
  135. res = set_control(dev, CONTROL_FILTER_ORIGINAL |
  136. CONTROL_MODE_OSD |
  137. CONTROL_ENABLE_ON);
  138. if (res) {
  139. debug("%s: Could not set control register\n", dev->name);
  140. return res;
  141. }
  142. return 0;
  143. }
  144. /**
  145. * div2_u16() - Approximately divide a 16-bit number by 2
  146. *
  147. * @val: The 16-bit value to divide by two
  148. * Return: The approximate division of val by two
  149. */
  150. static inline u16 div2_u16(u16 val)
  151. {
  152. return (32767 * val) / 65535;
  153. }
  154. int ihs_video_out_set_size(struct udevice *dev, uint col, uint row)
  155. {
  156. struct ihs_video_out_priv *priv = dev_get_priv(dev);
  157. if (!col || col > MAX_VIDEOMEM_WIDTH || col > MAX_X_CHARS ||
  158. !row || row > MAX_VIDEOMEM_HEIGHT || row > MAX_Y_CHARS) {
  159. debug("%s: Desired OSD size invalid\n", dev->name);
  160. return -EINVAL;
  161. }
  162. ihs_video_out_set(priv->map, xy_size, ((col - 1) << 8) | (row - 1));
  163. /* Center OSD on screen */
  164. ihs_video_out_set(priv->map, x_pos,
  165. div2_u16(priv->res_x - CHAR_WIDTH * col));
  166. ihs_video_out_set(priv->map, y_pos,
  167. div2_u16(priv->res_y - CHAR_HEIGHT * row));
  168. return 0;
  169. }
  170. int ihs_video_out_print(struct udevice *dev, uint col, uint row, ulong color,
  171. char *text)
  172. {
  173. int res;
  174. u8 buffer[2 * MAX_VIDEOMEM_WIDTH];
  175. uint k;
  176. uint charcount = strlen(text);
  177. uint len = min(charcount, 2 * MAX_VIDEOMEM_WIDTH);
  178. for (k = 0; k < len; ++k) {
  179. buffer[2 * k] = text[k];
  180. buffer[2 * k + 1] = color;
  181. }
  182. res = ihs_video_out_set_mem(dev, col, row, buffer, 2 * len, 1);
  183. if (res < 0) {
  184. debug("%s: Could not write to video memory\n", dev->name);
  185. return res;
  186. }
  187. return 0;
  188. }
  189. static const struct video_osd_ops ihs_video_out_ops = {
  190. .get_info = ihs_video_out_get_info,
  191. .set_mem = ihs_video_out_set_mem,
  192. .set_size = ihs_video_out_set_size,
  193. .print = ihs_video_out_print,
  194. };
  195. int ihs_video_out_probe(struct udevice *dev)
  196. {
  197. struct ihs_video_out_priv *priv = dev_get_priv(dev);
  198. struct ofnode_phandle_args phandle_args;
  199. const char *mode;
  200. u16 features;
  201. struct display_timing timing;
  202. int res;
  203. res = regmap_init_mem(dev_ofnode(dev), &priv->map);
  204. if (res) {
  205. debug("%s: Could not initialize regmap (err = %d)\n", dev->name,
  206. res);
  207. return res;
  208. }
  209. /* Range with index 2 is video memory */
  210. priv->vidmem = regmap_get_range(priv->map, 2);
  211. mode = dev_read_string(dev, "mode");
  212. if (!mode) {
  213. debug("%s: Could not read mode property\n", dev->name);
  214. return -EINVAL;
  215. }
  216. if (!strcmp(mode, "1024_768_60")) {
  217. priv->sync_src = 2;
  218. priv->res_x = 1024;
  219. priv->res_y = 768;
  220. timing.hactive.typ = 1024;
  221. timing.vactive.typ = 768;
  222. } else if (!strcmp(mode, "720_400_70")) {
  223. priv->sync_src = 1;
  224. priv->res_x = 720;
  225. priv->res_y = 400;
  226. timing.hactive.typ = 720;
  227. timing.vactive.typ = 400;
  228. } else {
  229. priv->sync_src = 0;
  230. priv->res_x = 640;
  231. priv->res_y = 480;
  232. timing.hactive.typ = 640;
  233. timing.vactive.typ = 480;
  234. }
  235. ihs_video_out_get(priv->map, features, &features);
  236. res = set_control(dev, CONTROL_FILTER_ORIGINAL |
  237. CONTROL_MODE_OSD |
  238. CONTROL_ENABLE_OFF);
  239. if (res) {
  240. debug("%s: Could not set control register (err = %d)\n",
  241. dev->name, res);
  242. return res;
  243. }
  244. priv->base_width = ((features & BASE_WIDTH_MASK)
  245. >> BASE_WIDTH_SHIFT) + 1;
  246. priv->base_height = ((features & BASE_HEIGTH_MASK)
  247. >> BASE_HEIGTH_SHIFT) + 1;
  248. res = dev_read_phandle_with_args(dev, "clk_gen", NULL, 0, 0,
  249. &phandle_args);
  250. if (res) {
  251. debug("%s: Could not get clk_gen node (err = %d)\n",
  252. dev->name, res);
  253. return -EINVAL;
  254. }
  255. res = uclass_get_device_by_ofnode(UCLASS_CLK, phandle_args.node,
  256. &priv->clk_gen);
  257. if (res) {
  258. debug("%s: Could not get clk_gen dev (err = %d)\n",
  259. dev->name, res);
  260. return -EINVAL;
  261. }
  262. res = dev_read_phandle_with_args(dev, "video_tx", NULL, 0, 0,
  263. &phandle_args);
  264. if (res) {
  265. debug("%s: Could not get video_tx (err = %d)\n",
  266. dev->name, res);
  267. return -EINVAL;
  268. }
  269. res = uclass_get_device_by_ofnode(UCLASS_DISPLAY, phandle_args.node,
  270. &priv->video_tx);
  271. if (res) {
  272. debug("%s: Could not get video_tx dev (err = %d)\n",
  273. dev->name, res);
  274. return -EINVAL;
  275. }
  276. res = display_enable(priv->video_tx, 8, &timing);
  277. if (res && res != -EIO) { /* Ignore missing DP sink error */
  278. debug("%s: Could not enable the display (err = %d)\n",
  279. dev->name, res);
  280. return res;
  281. }
  282. return 0;
  283. }
  284. U_BOOT_DRIVER(ihs_video_out_drv) = {
  285. .name = "ihs_video_out_drv",
  286. .id = UCLASS_VIDEO_OSD,
  287. .ops = &ihs_video_out_ops,
  288. .of_match = ihs_video_out_ids,
  289. .probe = ihs_video_out_probe,
  290. .priv_auto_alloc_size = sizeof(struct ihs_video_out_priv),
  291. };