{"id":2057,"date":"2010-01-05T21:46:01","date_gmt":"2010-01-06T05:46:01","guid":{"rendered":"http:\/\/multimedia.cx\/eggs\/?p=2057"},"modified":"2010-01-06T10:42:34","modified_gmt":"2010-01-06T18:42:34","slug":"installing-crystalhd-drivers-in-linux","status":"publish","type":"post","link":"https:\/\/multimedia.cx\/eggs\/installing-crystalhd-drivers-in-linux\/","title":{"rendered":"Installing CrystalHD Drivers In Linux"},"content":{"rendered":"<p><strong>Executive Summary:<\/strong> I tried to get a Broadcom CrystalHD chip to work in Linux. I came close to being successful. The chip, kernel driver, and userspace library all work. The example app that would have been the payoff&#8230; not so much. I document my process in this post in case others need assistance, or can lend assistance in the final step.<\/p>\n<p>There was <a href=\"http:\/\/xbmc.org\/davilla\/2009\/12\/29\/broadcom-crystal-hd-its-magic\/\">some news recently about Broadcom open sourcing code related to a video decoding chip<\/a>. The brand name here is apparently &#8220;CrystalHD&#8221; and the chip in question is the BCM-70012. I came into possession of one of these and endeavored to make the open source software surrounding it work in Linux.<\/p>\n<p>The first issue is installing the hardware. It&#8217;s a PCI Express mini card which has the same form factor as a PCIe mini wireless networking card. So if your computer can host such a wireless card, it can also hold this thing. <em>Allegedly<\/em>. First, I tried to place it in the empty PCIe-mini slot in my MSI Wind Nettop. No go. The machine refused to boot up (it would power up but never beep to indicate that it&#8217;s really ready to run). Removing the card made the problem go away.<\/p>\n<p>So determined was I to make this chip work that I actually took apart my dear Eee PC 701, ripped out the wireless card and replaced it with the Broadcom card. Deciding it would be too much trouble to attempt to re-attach the keyboard and touchpad ribbons, I realized I could just use USB peripherals.<\/p>\n<p>Resigned to the notion that I just foolishly destroyed my 2 year old Eee PC, I threw the switch anyway and was quite surprised to see it boot up normally. An &#8216;lspci&#8217; command indicates a new Broadcom multimedia controller hanging off the PCI bus. It&#8217;s not pretty but it&#8217;s breathing:<\/p>\n<p><center><br \/>\n<img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2010\/01\/eee-pc-701-broadcom-crystalhd.JPG\" alt=\"Eee PC 701 disassembled with Broadcom CrystalHD decoder installed\" title=\"Eee PC 701 disassembled with Broadcom CrystalHD decoder installed\" width=\"400\" height=\"457\" class=\"aligncenter size-full wp-image-2058\" srcset=\"https:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2010\/01\/eee-pc-701-broadcom-crystalhd.JPG 400w, https:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2010\/01\/eee-pc-701-broadcom-crystalhd-262x300.jpg 262w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/><br \/>\n<\/center><\/p>\n<p>So let&#8217;s talk software. Broadcom released the driver as open source. To many in the open source community, this is tantamount to, &#8220;Okay, done deal! What else needs to be open sourced?&#8221; Not so fast. There&#8217;s no documentation in the whole package (user-wise, anyway; the libcrystalhd API is thoroughly documented in header comments). So I will describe my experiences with the software.<\/p>\n<p><!--more--><\/p>\n<p><a href=\"http:\/\/www.broadcom.com\/support\/crystal_hd\/\">Download the Linux driver package and unpack it.<\/a><\/p>\n<p>Have some kernel source to build against. You don&#8217;t actually need a full source tree installed; just the headers will do. To install the appropriate header package on an Ubuntu system, execute:<\/p>\n<pre>\r\n  apt-get install linux-headers-`uname -r`\r\n<\/pre>\n<p>When installing a linux-headers package, it&#8217;s necessary to specify a kernel version. `uname -r` will determine your Linux kernel version and substitute it into the command line.<\/p>\n<p>If you&#8217;re going the route of installing only kernel headers like I did, you will also need to create a symlink from \/lib\/modules\/&lt;version&gt;\/build to the new headers tree. This should do the trick:<\/p>\n<pre>\r\n  ln -s \/usr\/src\/linux-headers-`uname -r` \/lib\/modules\/`uname -r`\/build\r\n<\/pre>\n<p>With that infrastructure in place, change directory into crystalhd\/driver\/linux. <strong>Important step:<\/strong> Run &#8216;dos2unix *&#8217; in this directory. There are DOS-style line endings running around in here that will foul up the build process. Run &#8216;autoconf&#8217;. Then run &#8216;.\/configure&#8217;. The whole process looks sort of like this:<\/p>\n<pre>\r\n$ dos2unix *\r\n$ autoconf \r\n$ .\/configure \r\nchecking for ld... ld\r\nconfigure: creating .\/config.status\r\nconfig.status: creating .\/Makefile\r\n<\/pre>\n<p>Time to build: &#8216;make&#8217;. This should look something like:<\/p>\n<pre>\r\n$ make\r\nmake -C \/lib\/modules\/2.6.30.5-ep0\/build SUBDIRS=\/home\/melanson\/crystalhd\/driver\/linux modules\r\nmake[1]: Entering directory `\/usr\/src\/linux-headers-2.6.30.5-ep0'\r\n  CC [M]  \/home\/melanson\/crystalhd\/driver\/linux\/crystalhd_lnx.o\r\n  CC [M]  \/home\/melanson\/crystalhd\/driver\/linux\/crystalhd_misc.o\r\n  CC [M]  \/home\/melanson\/crystalhd\/driver\/linux\/crystalhd_cmds.o\r\n  CC [M]  \/home\/melanson\/crystalhd\/driver\/linux\/crystalhd_hw.o\r\n  LD [M]  \/home\/melanson\/crystalhd\/driver\/linux\/crystalhd.o\r\n  Building modules, stage 2.\r\n  MODPOST 1 modules\r\n  CC      \/home\/melanson\/crystalhd\/driver\/linux\/crystalhd.mod.o\r\n  LD [M]  \/home\/melanson\/crystalhd\/driver\/linux\/crystalhd.ko\r\nmake[1]: Leaving directory `\/usr\/src\/linux-headers-2.6.30.5-ep0'\r\n<\/pre>\n<p>&#8216;make install&#8217; (as root, of course) to get the module where it needs to go and then &#8216;modprobe crystalhd&#8217; to activate the driver. After the modprobe command, the last line of my &#8216;dmesg&#8217; log looks like:<\/p>\n<pre>\r\n  Broadcom 70012 Decoder 0000:01:00.0: setting latency timer to 64\r\n<\/pre>\n<p>We&#8217;re not done yet, though. There are 2 provided scripts (bcm_70012_???.sh) which are supposed to load the module and create a device node. The scripts failed to run. Instead, I sorted out the right command for my machine:<\/p>\n<pre>\r\nmknod -m 666 \/dev\/crystalhd c 251 0\r\n<\/pre>\n<p>The major number (251) comes from &#8216;grep crystalhd \/proc\/devices&#8217;.<\/p>\n<p>Then we move over to the user-land system library (crystalhd\/linux_lib\/libcrystalhd). A simple &#8216;make &#038;&#038; sudo make install&#8217; should do the trick here.<\/p>\n<p>Then there&#8217;s the matter of the example code (crystalhd\/examples). Whoever wrote these samples was not in close communication with the people who wrote the rest of the library. It&#8217;s reasonable that the code did work at some point. But whoever made this final package reorganized the directory structure and didn&#8217;t test this code since there are all kinds of problems when running the simple build script included.<\/p>\n<p>Things went off the rails at this point.<\/p>\n<p>The build script doesn&#8217;t build as-is due to incorrect paths. I changed the build script to look like this:<\/p>\n<p>g++ -I ..\/include\/ -I ..\/linux_lib\/libcrystalhd\/ -I\/usr\/include -D__LINUX_USER__ -lcrystalhd -lpthread hellobcm.cpp -o hellobcm<\/p>\n<p>This is to be run from the examples directory. Doing so, however, indicates that hellobcm.cpp has several problems. Upshot: I altered the start of the file to look like this (added 2 headers, subtracted one, added U8 and U32 typedefs which might only work on x86_32):<\/p>\n<pre>\r\n#include &lt;stdlib .h&gt;\r\n#include &lt;stdio .h&gt;\r\n#include &lt;stdint .h&gt;\r\n#include &lt;string .h&gt;\r\n#include &lt;semaphore .h&gt;\r\n#include \"bc_dts_types.h\"\r\n#include \"bc_dts_defs.h\"\r\n#include \"libcrystalhd_if.h\"\r\n\/\/#include \"bc_ldil_if.h\"\r\n#include &lt;iostream&gt;\r\n#include &lt;fstream&gt;\r\n#include &lt;sys \/shm.h&gt;\r\n\r\ntypedef unsigned char U8;\r\ntypedef unsigned int U32;\r\n\r\n#define TRY_CALL_1(func, p1, errmsg) \\\r\n[...]\r\n<\/pre>\n<p>That gets the test program to compile. Running it results in a complaint that firmware can&#8217;t be located in a preordained location:<\/p>\n<pre>\r\n[...]\r\nDtsGetFirmwareFiles:Ctx->FwBinFile is \/lib\/firmware\/bcmFilePlayFw.bin\r\nFailed to Open FW file\r\n[...]\r\n<\/pre>\n<p>All right, so it wants the firmware files in very specific places (this is actually due to that libcrystalhd user-land library). These firmware files live in crystalhd\/firmware\/fwbin\/70012. Moving the firmware files to \/lib\/firmware (hey! there are a bunch of firmware files there; I never knew that), makes hellobcm get farther before tossing out a cryptic error. A little investigation reveals that the source is looking for a hardcoded .264 video file (home directory paths implicate a &#8216;jarod&#8217; and a &#8216;davilla&#8217;, perhaps the same davilla who <a href=\"http:\/\/xbmc.org\/davilla\/2009\/12\/29\/broadcom-crystal-hd-its-magic\/\">authored the XBMC blog post<\/a>).<\/p>\n<p>So I need to feed it a raw .264 video file. All right, Broadcom, let&#8217;s see what you&#8217;ve got. Chew on the <a href=\"http:\/\/fate.multimedia.cx\/index.php?test_spec=200\">MV1_BRCM_1 H.264 sample<\/a> (<a href=\"http:\/\/samples.mplayerhq.hu\/fate-suite\/h264-conformance\/\">src19td.IBP.264 from here<\/a>), one of the meanest conformance samples in the ITU suite. Incidentally, it&#8217;s from Broadcom (hence, &#8220;BRCM&#8221;).<\/p>\n<p>Things go&#8230; well, confusingly. The program emits a lot of debugging info which largely emanates from the libcrystalhd.so library (that&#8217;s also the code which downloads the firmware at initialization). I&#8217;m still massaging the code (various other parameters seem to be hardcoded based on the sample) but when it does run successfully, it claims to decode 136\/257 frames from the sample. And it does so while occupying the CPU for about a quarter of a second.<\/p>\n<p>For comparison, it takes <a href=\"http:\/\/ffmpeg.org\/\">FFmpeg<\/a> 16.6 seconds of CPU time to decode this sample using the following command line:<\/p>\n<pre>\r\n  ffmpeg -benchmark -i src19td.IBP.264 -f yuv4mpegpipe -y \/dev\/null\r\n<\/pre>\n<p>It is, of course, inappropriate to make comparisons before I can validate that this example is decoding anything correctly (and it&#8217;s a specious comparison anyway). It should be noted that 16.6s amounts to &#8220;less than realtime&#8221; for a sample that&#8217;s 10 seconds long. So that&#8217;s the time to beat on my Eee PC 701 equipped with an Intel Celeron M CPU running at 620 MHz&#8230; wait, no, <strong>ACK!<\/strong> \/proc\/cpuinfo reports the full 900 MHz! I accidentally overclocked the thing. Or rather, de-underclocked it. Oops. Per my understanding, the only negative implication of that is less battery time.<\/p>\n<p>This same CrystalHD code seems to have been copy and pasted for <a href=\"http:\/\/code.google.com\/p\/crystalhd-for-osx\/\">crystalhd-for-osx<\/a>. The same example programs live in that tree. I doubt they compile.<\/p>\n<p>Would CrystalHD driver support be useful for FFmpeg? It doesn&#8217;t really seem like a good fit. But then, FFmpeg already supports other hardware decoding features such as XvMC and VDPAU.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A chronicle of my effort to get a CrystalHD chip working in Linux<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-2057","post","type-post","status-publish","format-standard","hentry","category-open-source-multimedia"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/posts\/2057","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/comments?post=2057"}],"version-history":[{"count":10,"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/posts\/2057\/revisions"}],"predecessor-version":[{"id":2068,"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/posts\/2057\/revisions\/2068"}],"wp:attachment":[{"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/media?parent=2057"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/categories?post=2057"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/tags?post=2057"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}