omapfb.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. /*
  2. * Copyright (C) 2008-2010 Felipe Contreras
  3. *
  4. * Author: Felipe Contreras <felipe.contreras@gmail.com>
  5. *
  6. * This file may be used under the terms of the GNU Lesser General Public
  7. * License version 2.1, a copy of which is found in LICENSE included in the
  8. * packaging of this file.
  9. */
  10. #include <fcntl.h>
  11. #include <sys/ioctl.h>
  12. #include <sys/mman.h>
  13. #include <unistd.h>
  14. #include <string.h>
  15. #include <stdbool.h>
  16. #include <gst/gst.h>
  17. #include <gst/base/gstbasesink.h>
  18. #include <linux/fb.h>
  19. #include <linux/omapfb.h>
  20. #include "omapfb.h"
  21. #include "log.h"
  22. #define ROUND_UP(num, scale) (((num) + ((scale) - 1)) & ~((scale) - 1))
  23. static void *parent_class;
  24. #ifndef GST_DISABLE_GST_DEBUG
  25. GstDebugCategory *omapfb_debug;
  26. #endif
  27. struct page {
  28. unsigned yoffset;
  29. void *buf;
  30. bool used;
  31. };
  32. struct gst_omapfb_sink {
  33. GstBaseSink parent;
  34. struct fb_var_screeninfo varinfo;
  35. struct fb_var_screeninfo overlay_info;
  36. struct omapfb_mem_info mem_info;
  37. struct omapfb_plane_info plane_info;
  38. int overlay_fd;
  39. unsigned char *framebuffer;
  40. bool enabled;
  41. bool manual_update;
  42. struct page *pages;
  43. int nr_pages;
  44. struct page *cur_page;
  45. struct page *old_page;
  46. };
  47. struct gst_omapfb_sink_class {
  48. GstBaseSinkClass parent_class;
  49. };
  50. static GstCaps *
  51. generate_sink_template(void)
  52. {
  53. GstCaps *caps;
  54. GstStructure *struc;
  55. caps = gst_caps_new_empty();
  56. struc = gst_structure_new("video/x-raw-yuv",
  57. "width", GST_TYPE_INT_RANGE, 16, 4096,
  58. "height", GST_TYPE_INT_RANGE, 16, 4096,
  59. "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 30, 1,
  60. NULL);
  61. {
  62. GValue list;
  63. GValue val;
  64. list.g_type = val.g_type = 0;
  65. g_value_init(&list, GST_TYPE_LIST);
  66. g_value_init(&val, GST_TYPE_FOURCC);
  67. #if 0
  68. gst_value_set_fourcc(&val, GST_MAKE_FOURCC('I', '4', '2', '0'));
  69. gst_value_list_append_value(&list, &val);
  70. gst_value_set_fourcc(&val, GST_MAKE_FOURCC('Y', 'U', 'Y', '2'));
  71. gst_value_list_append_value(&list, &val);
  72. gst_value_set_fourcc(&val, GST_MAKE_FOURCC('U', 'Y', 'V', 'Y'));
  73. gst_value_list_append_value(&list, &val);
  74. #else
  75. gst_value_set_fourcc(&val, GST_MAKE_FOURCC('U', 'Y', 'V', 'Y'));
  76. gst_value_list_append_value(&list, &val);
  77. #endif
  78. gst_structure_set_value(struc, "format", &list);
  79. g_value_unset(&val);
  80. g_value_unset(&list);
  81. }
  82. gst_caps_append_structure(caps, struc);
  83. return caps;
  84. }
  85. static void
  86. update(struct gst_omapfb_sink *self)
  87. {
  88. struct omapfb_update_window update_window;
  89. unsigned x, y, w, h;
  90. x = y = 0;
  91. w = self->varinfo.xres;
  92. h = self->varinfo.yres;
  93. update_window.x = x;
  94. update_window.y = y;
  95. update_window.width = w;
  96. update_window.height = h;
  97. update_window.format = 0;
  98. update_window.out_x = 0;
  99. update_window.out_y = 0;
  100. update_window.out_width = w;
  101. update_window.out_height = h;
  102. ioctl(self->overlay_fd, OMAPFB_UPDATE_WINDOW, &update_window);
  103. }
  104. static struct page *get_page(struct gst_omapfb_sink *self)
  105. {
  106. struct page *page = NULL;
  107. int i;
  108. for (i = 0; i < self->nr_pages; i++) {
  109. if (&self->pages[i] == self->cur_page)
  110. continue;
  111. if (&self->pages[i] == self->old_page)
  112. continue;
  113. if (!self->pages[i].used) {
  114. page = &self->pages[i];
  115. break;
  116. }
  117. }
  118. /* old page needs a vsync */
  119. if (!page && self->old_page && !self->old_page->used)
  120. page = self->old_page;
  121. if (page)
  122. page->used = true;
  123. return page;
  124. }
  125. static gboolean
  126. setup(struct gst_omapfb_sink *self, GstCaps *caps)
  127. {
  128. GstStructure *structure;
  129. int width, height;
  130. int update_mode;
  131. struct omapfb_color_key color_key;
  132. size_t framesize;
  133. int par_n, par_d;
  134. unsigned out_width, out_height;
  135. structure = gst_caps_get_structure(caps, 0);
  136. gst_structure_get_int(structure, "width", &width);
  137. gst_structure_get_int(structure, "height", &height);
  138. if (!gst_structure_get_fraction(structure, "pixel-aspect-ratio", &par_n, &par_d))
  139. par_n = par_d = 1;
  140. self->plane_info.enabled = 0;
  141. if (ioctl(self->overlay_fd, OMAPFB_SETUP_PLANE, &self->plane_info)) {
  142. pr_err(self, "could not disable plane");
  143. return false;
  144. }
  145. framesize = GST_ROUND_UP_2(width) * height * 2;
  146. self->mem_info.type = OMAPFB_MEMTYPE_SDRAM;
  147. self->mem_info.size = framesize * self->nr_pages;
  148. if (ioctl(self->overlay_fd, OMAPFB_SETUP_MEM, &self->mem_info)) {
  149. pr_err(self, "could not setup memory info");
  150. return false;
  151. }
  152. self->framebuffer = mmap(NULL, self->mem_info.size, PROT_WRITE, MAP_SHARED, self->overlay_fd, 0);
  153. if (self->framebuffer == MAP_FAILED) {
  154. pr_err(self, "memory map failed");
  155. return false;
  156. }
  157. self->overlay_info.xres = width;
  158. self->overlay_info.yres = height;
  159. self->overlay_info.xres_virtual = self->overlay_info.xres;
  160. self->overlay_info.yres_virtual = self->overlay_info.yres * self->nr_pages;
  161. self->overlay_info.xoffset = 0;
  162. self->overlay_info.yoffset = 0;
  163. self->overlay_info.nonstd = OMAPFB_COLOR_YUV422;
  164. pr_info(self, "vscreen info: width=%u, height=%u",
  165. self->overlay_info.xres, self->overlay_info.yres);
  166. if (ioctl(self->overlay_fd, FBIOPUT_VSCREENINFO, &self->overlay_info)) {
  167. pr_err(self, "could not set screen info");
  168. return false;
  169. }
  170. color_key.key_type = OMAPFB_COLOR_KEY_DISABLED;
  171. if (ioctl(self->overlay_fd, OMAPFB_SET_COLOR_KEY, &color_key))
  172. pr_err(self, "could not disable color key");
  173. /* scale to width */
  174. out_width = self->varinfo.xres;
  175. out_height = (height * par_d * self->varinfo.xres + width * par_n / 2) / (width * par_n);
  176. if (out_height > self->varinfo.yres) {
  177. /* scale to height */
  178. out_height = self->varinfo.yres;
  179. out_width = (width * par_n * self->varinfo.yres + height * par_d / 2) / (height * par_d);
  180. }
  181. out_width = ROUND_UP(out_width, 2);
  182. out_height = ROUND_UP(out_height, 2);
  183. self->plane_info.enabled = 1;
  184. self->plane_info.pos_x = (self->varinfo.xres - out_width) / 2;
  185. self->plane_info.pos_y = (self->varinfo.yres - out_height) / 2;
  186. self->plane_info.out_width = out_width;
  187. self->plane_info.out_height = out_height;
  188. pr_info(self, "output info: %dx%d, offset: %d,%d",
  189. out_width, out_height,
  190. self->plane_info.pos_x, self->plane_info.pos_y);
  191. pr_info(self, "plane info: %ux%u",
  192. self->varinfo.xres, self->varinfo.yres);
  193. if (ioctl(self->overlay_fd, OMAPFB_SETUP_PLANE, &self->plane_info)) {
  194. pr_err(self, "could not setup plane");
  195. return false;
  196. }
  197. self->enabled = true;
  198. update_mode = OMAPFB_MANUAL_UPDATE;
  199. ioctl(self->overlay_fd, OMAPFB_SET_UPDATE_MODE, &update_mode);
  200. self->manual_update = (update_mode == OMAPFB_MANUAL_UPDATE);
  201. self->pages = calloc(self->nr_pages, sizeof(*self->pages));
  202. int i;
  203. for (i = 0; i < self->nr_pages; i++) {
  204. self->pages[i].yoffset = i * self->overlay_info.yres;
  205. self->pages[i].buf = self->framebuffer + (i * framesize);
  206. }
  207. self->cur_page = &self->pages[0];
  208. return true;
  209. }
  210. static GstFlowReturn
  211. buffer_alloc(GstBaseSink *base, guint64 offset, guint size, GstCaps *caps, GstBuffer **buf)
  212. {
  213. struct gst_omapfb_sink *self = (struct gst_omapfb_sink *)base;
  214. GstBuffer *buffer;
  215. struct page *page;
  216. if (!self->enabled && !setup(self, caps))
  217. goto missing;
  218. page = get_page(self);
  219. if (!page)
  220. goto missing;
  221. buffer = gst_buffer_new();
  222. GST_BUFFER_DATA(buffer) = page->buf;
  223. GST_BUFFER_SIZE(buffer) = size;
  224. gst_buffer_set_caps(buffer, caps);
  225. *buf = buffer;
  226. if (page == self->old_page)
  227. ioctl(self->overlay_fd, OMAPFB_WAITFORVSYNC);
  228. return GST_FLOW_OK;
  229. missing:
  230. *buf = NULL;
  231. return GST_FLOW_OK;
  232. }
  233. static gboolean
  234. setcaps(GstBaseSink *base, GstCaps *caps)
  235. {
  236. struct gst_omapfb_sink *self = (struct gst_omapfb_sink *)base;
  237. if (self->enabled)
  238. return true;
  239. return setup(self, caps);
  240. }
  241. static gboolean
  242. start(GstBaseSink *base)
  243. {
  244. struct gst_omapfb_sink *self = (struct gst_omapfb_sink *)base;
  245. int fd;
  246. self->nr_pages = 4;
  247. self->cur_page = self->old_page = NULL;
  248. fd = open("/dev/fb0", O_RDWR);
  249. if (fd == -1) {
  250. pr_err(self, "could not open framebuffer");
  251. return false;
  252. }
  253. if (ioctl(fd, FBIOGET_VSCREENINFO, &self->varinfo)) {
  254. pr_err(self, "could not get screen info");
  255. close(fd);
  256. return false;
  257. }
  258. if (close(fd)) {
  259. pr_err(self, "could not close framebuffer");
  260. return false;
  261. }
  262. self->overlay_fd = open("/dev/fb1", O_RDWR);
  263. if (self->overlay_fd == -1) {
  264. pr_err(self, "could not open overlay");
  265. return false;
  266. }
  267. if (ioctl(self->overlay_fd, FBIOGET_VSCREENINFO, &self->overlay_info)) {
  268. pr_err(self, "could not get overlay screen info");
  269. return false;
  270. }
  271. if (ioctl(self->overlay_fd, OMAPFB_QUERY_PLANE, &self->plane_info)) {
  272. pr_err(self, "could not query plane info");
  273. return false;
  274. }
  275. return true;
  276. }
  277. static gboolean
  278. stop(GstBaseSink *base)
  279. {
  280. struct gst_omapfb_sink *self = (struct gst_omapfb_sink *)base;
  281. if (self->enabled) {
  282. self->plane_info.enabled = 0;
  283. if (ioctl(self->overlay_fd, OMAPFB_SETUP_PLANE, &self->plane_info)) {
  284. pr_err(self, "could not disable plane");
  285. return false;
  286. }
  287. }
  288. if (munmap(self->framebuffer, self->mem_info.size)) {
  289. pr_err(self, "could not unmap");
  290. return false;
  291. }
  292. if (close(self->overlay_fd)) {
  293. pr_err(self, "could not close overlay");
  294. return false;
  295. }
  296. return true;
  297. }
  298. static GstFlowReturn
  299. render(GstBaseSink *base, GstBuffer *buffer)
  300. {
  301. struct gst_omapfb_sink *self = (struct gst_omapfb_sink *)base;
  302. struct page *page = NULL;
  303. int i;
  304. for (i = 0; i < self->nr_pages; i++)
  305. if (self->pages[i].buf == GST_BUFFER_DATA(buffer)) {
  306. page = &self->pages[i];
  307. break;
  308. }
  309. if (!page) {
  310. page = get_page(self);
  311. if (!page)
  312. page = self->cur_page; /* not ok, but last resort */
  313. memcpy(page->buf, GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer));
  314. }
  315. if (page != self->cur_page) {
  316. self->overlay_info.yoffset = page->yoffset;
  317. ioctl(self->overlay_fd, FBIOPAN_DISPLAY, &self->overlay_info);
  318. }
  319. if (self->manual_update)
  320. update(self);
  321. self->old_page = self->cur_page;
  322. self->cur_page = page;
  323. page->used = false;
  324. return GST_FLOW_OK;
  325. }
  326. static void
  327. class_init(void *g_class, void *class_data)
  328. {
  329. GstBaseSinkClass *base_sink_class;
  330. base_sink_class = g_class;
  331. parent_class = g_type_class_ref(GST_OMAPFB_SINK_TYPE);
  332. base_sink_class->set_caps = setcaps;
  333. base_sink_class->buffer_alloc = buffer_alloc;
  334. base_sink_class->start = start;
  335. base_sink_class->stop = stop;
  336. base_sink_class->render = render;
  337. base_sink_class->preroll = render;
  338. }
  339. static void
  340. base_init(void *g_class)
  341. {
  342. GstElementClass *element_class = g_class;
  343. GstPadTemplate *template;
  344. gst_element_class_set_details_simple(element_class,
  345. "Linux OMAP framebuffer sink",
  346. "Sink/Video",
  347. "Renders video with omapfb",
  348. "Felipe Contreras");
  349. template = gst_pad_template_new("sink", GST_PAD_SINK,
  350. GST_PAD_ALWAYS,
  351. generate_sink_template());
  352. gst_element_class_add_pad_template(element_class, template);
  353. }
  354. GType
  355. gst_omapfb_sink_get_type(void)
  356. {
  357. static GType type;
  358. if (G_UNLIKELY(type == 0)) {
  359. GTypeInfo type_info = {
  360. .class_size = sizeof(struct gst_omapfb_sink_class),
  361. .class_init = class_init,
  362. .base_init = base_init,
  363. .instance_size = sizeof(struct gst_omapfb_sink),
  364. };
  365. type = g_type_register_static(GST_TYPE_BASE_SINK, "GstOmapFbSink", &type_info, 0);
  366. }
  367. return type;
  368. }
  369. static gboolean
  370. plugin_init(GstPlugin *plugin)
  371. {
  372. #ifndef GST_DISABLE_GST_DEBUG
  373. omapfb_debug = _gst_debug_category_new("omapfb", 0, "omapfb");
  374. #endif
  375. if (!gst_element_register(plugin, "omapfbsink", GST_RANK_SECONDARY, GST_OMAPFB_SINK_TYPE))
  376. return false;
  377. return true;
  378. }
  379. GstPluginDesc gst_plugin_desc = {
  380. .major_version = GST_VERSION_MAJOR,
  381. .minor_version = GST_VERSION_MINOR,
  382. .name = "omapfb",
  383. .description = (gchar *) "Linux OMAP framebuffer",
  384. .plugin_init = plugin_init,
  385. .version = VERSION,
  386. .license = "LGPL",
  387. .source = "source",
  388. .package = "package",
  389. .origin = "origin",
  390. };