vserver 2.0 rc7
[linux-2.6.git] / drivers / media / video / saa7134 / saa6752hs.c
index 5fdff48..1db0226 100644 (file)
 #include <linux/types.h>
 #include <linux/videodev.h>
 #include <linux/init.h>
+#include <linux/crc32.h>
 
 #include <media/id.h>
-#include <media/saa6752hs.h>
+
+#define MPEG_VIDEO_TARGET_BITRATE_MAX  27000
+#define MPEG_VIDEO_MAX_BITRATE_MAX     27000
+#define MPEG_TOTAL_TARGET_BITRATE_MAX  27000
+#define MPEG_PID_MAX ((1 << 14) - 1)
 
 /* Addresses to scan */
 static unsigned short normal_i2c[] = {0x20, I2C_CLIENT_END};
@@ -27,6 +32,33 @@ MODULE_LICENSE("GPL");
 static struct i2c_driver driver;
 static struct i2c_client client_template;
 
+enum saa6752hs_videoformat {
+       SAA6752HS_VF_D1 = 0,    /* standard D1 video format: 720x576 */
+       SAA6752HS_VF_2_3_D1 = 1,/* 2/3D1 video format: 480x576 */
+       SAA6752HS_VF_1_2_D1 = 2,/* 1/2D1 video format: 352x576 */
+       SAA6752HS_VF_SIF = 3,   /* SIF video format: 352x288 */
+       SAA6752HS_VF_UNKNOWN,
+};
+
+static const struct v4l2_format v4l2_format_table[] =
+{
+       [SAA6752HS_VF_D1] = {
+               .fmt = { .pix = { .width = 720, .height = 576 }, }, },
+       [SAA6752HS_VF_2_3_D1] = {
+               .fmt = { .pix = { .width = 480, .height = 576 }, }, },
+       [SAA6752HS_VF_1_2_D1] = {
+               .fmt = { .pix = { .width = 352, .height = 576 }, }, },
+       [SAA6752HS_VF_SIF] = {
+               .fmt = { .pix = { .width = 352, .height = 288 }, }, },
+       [SAA6752HS_VF_UNKNOWN] = {
+               .fmt = { .pix = { .width = 0, .height = 0 }, }, },
+};
+
+struct saa6752hs_state {
+       struct i2c_client             client;
+       struct v4l2_mpeg_compression  params;
+       enum saa6752hs_videoformat    video_format;
+};
 
 enum saa6752hs_command {
        SAA6752HS_COMMAND_RESET = 0,
@@ -40,7 +72,6 @@ enum saa6752hs_command {
        SAA6752HS_COMMAND_MAX
 };
 
-
 /* ---------------------------------------------------------------------- */
 
 static u8 PAT[] = {
@@ -64,9 +95,9 @@ static u8 PAT[] = {
 
        0x00, 0x01, // program_number(1)
 
-       0xe0, 0x10, // PMT PID(0x10)
+       0xe0, 0x00, // PMT PID
 
-       0x76, 0xf1, 0x44, 0xd1 // CRC32
+       0x00, 0x00, 0x00, 0x00 // CRC32
 };
 
 static u8 PMT[] = {
@@ -74,7 +105,7 @@ static u8 PMT[] = {
        0x01, // table number for encoder
 
        0x47, // sync
-       0x40, 0x10, // transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid(0x10)
+       0x40, 0x00, // transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid
        0x10, // transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0)
 
        0x00, // PSI pointer to start of table
@@ -88,28 +119,51 @@ static u8 PMT[] = {
 
        0x00, 0x00, // section_number(0), last_section_number(0)
 
-       0xe1, 0x04, // PCR_PID (0x104)
+       0xe0, 0x00, // PCR_PID
 
        0xf0, 0x00, // program_info_length(0)
 
-       0x02, 0xe1, 0x00, 0xf0, 0x00, // video stream type(2), pid(0x100)
-       0x04, 0xe1, 0x03, 0xf0, 0x00, // audio stream type(4), pid(0x103)
+       0x02, 0xe0, 0x00, 0xf0, 0x00, // video stream type(2), pid
+       0x04, 0xe0, 0x00, 0xf0, 0x00, // audio stream type(4), pid
 
-       0xa1, 0xca, 0x0f, 0x82 // CRC32
+       0x00, 0x00, 0x00, 0x00 // CRC32
 };
 
-static struct mpeg_params mpeg_params_template =
+static struct v4l2_mpeg_compression param_defaults =
 {
-       .bitrate_mode = MPEG_BITRATE_MODE_CBR,
-       .video_target_bitrate = 5000,
-       .audio_bitrate = MPEG_AUDIO_BITRATE_256,
-       .total_bitrate = 6000,
+       .st_type         = V4L2_MPEG_TS_2,
+       .st_bitrate      = {
+               .mode    = V4L2_BITRATE_CBR,
+               .target  = 7000,
+       },
+
+       .ts_pid_pmt      = 16,
+       .ts_pid_video    = 260,
+       .ts_pid_audio    = 256,
+       .ts_pid_pcr      = 259,
+
+       .vi_type         = V4L2_MPEG_VI_2,
+       .vi_aspect_ratio = V4L2_MPEG_ASPECT_4_3,
+       .vi_bitrate      = {
+               .mode    = V4L2_BITRATE_VBR,
+               .target  = 4000,
+               .max     = 6000,
+       },
+
+       .au_type         = V4L2_MPEG_AU_2_II,
+       .au_bitrate      = {
+               .mode    = V4L2_BITRATE_CBR,
+               .target  = 256,
+       },
+
+#if 0
+       /* FIXME: size? via S_FMT? */
+       .video_format = MPEG_VIDEO_FORMAT_D1,
+#endif
 };
 
-
 /* ---------------------------------------------------------------------- */
 
-
 static int saa6752hs_chip_command(struct i2c_client* client,
                                  enum saa6752hs_command command)
 {
@@ -124,7 +178,7 @@ static int saa6752hs_chip_command(struct i2c_client* client,
                break;
 
        case SAA6752HS_COMMAND_STOP:
-                       buf[0] = 0x03;
+               buf[0] = 0x03;
                break;
 
        case SAA6752HS_COMMAND_START:
@@ -180,74 +234,162 @@ static int saa6752hs_chip_command(struct i2c_client* client,
 
 
 static int saa6752hs_set_bitrate(struct i2c_client* client,
-                                struct mpeg_params* params)
+                                struct v4l2_mpeg_compression* params)
 {
        u8 buf[3];
 
        // set the bitrate mode
        buf[0] = 0x71;
-       buf[1] = params->bitrate_mode;
+       buf[1] = (params->vi_bitrate.mode == V4L2_BITRATE_VBR) ? 0 : 1;
        i2c_master_send(client, buf, 2);
 
        // set the video bitrate
-       if (params->bitrate_mode == MPEG_BITRATE_MODE_VBR) {
+       if (params->vi_bitrate.mode == V4L2_BITRATE_VBR) {
                // set the target bitrate
                buf[0] = 0x80;
-               buf[1] = params->video_target_bitrate >> 8;
-               buf[2] = params->video_target_bitrate & 0xff;
+               buf[1] = params->vi_bitrate.target >> 8;
+               buf[2] = params->vi_bitrate.target & 0xff;
                i2c_master_send(client, buf, 3);
 
                // set the max bitrate
                buf[0] = 0x81;
-               buf[1] = params->video_max_bitrate >> 8;
-               buf[2] = params->video_max_bitrate & 0xff;
+               buf[1] = params->vi_bitrate.max >> 8;
+               buf[2] = params->vi_bitrate.max & 0xff;
                i2c_master_send(client, buf, 3);
        } else {
                // set the target bitrate (no max bitrate for CBR)
                buf[0] = 0x81;
-               buf[1] = params->video_target_bitrate >> 8;
-               buf[2] = params->video_target_bitrate & 0xff;
+               buf[1] = params->vi_bitrate.target >> 8;
+               buf[2] = params->vi_bitrate.target & 0xff;
                i2c_master_send(client, buf, 3);
        }
 
        // set the audio bitrate
        buf[0] = 0x94;
-       buf[1] = params->audio_bitrate;
+       buf[1] = (256 == params->au_bitrate.target) ? 0 : 1;
        i2c_master_send(client, buf, 2);
 
        // set the total bitrate
        buf[0] = 0xb1;
-       buf[1] = params->total_bitrate >> 8;
-       buf[2] = params->total_bitrate & 0xff;
+       buf[1] = params->st_bitrate.target >> 8;
+       buf[2] = params->st_bitrate.target & 0xff;
        i2c_master_send(client, buf, 3);
 
+       // return success
        return 0;
 }
 
-
-static int saa6752hs_init(struct i2c_client* client, struct mpeg_params* params)
+static void saa6752hs_set_subsampling(struct i2c_client* client,
+                                     struct v4l2_format* f)
 {
-       unsigned char buf[3];
-       void *data;
-
-       // check the bitrate parameters first
-       if (params != NULL) {
-               if (params->bitrate_mode >= MPEG_BITRATE_MODE_MAX)
-                       return -EINVAL;
-               if (params->video_target_bitrate >= MPEG_VIDEO_TARGET_BITRATE_MAX)
-                       return -EINVAL;
-               if (params->video_max_bitrate >= MPEG_VIDEO_MAX_BITRATE_MAX)
-                       return -EINVAL;
-               if (params->audio_bitrate >= MPEG_AUDIO_BITRATE_MAX)
-                       return -EINVAL;
-               if (params->total_bitrate >= MPEG_TOTAL_BITRATE_MAX)
-                       return -EINVAL;
-               if (params->bitrate_mode         == MPEG_BITRATE_MODE_MAX &&
-                   params->video_target_bitrate <= params->video_max_bitrate)
-                       return -EINVAL;
+       struct saa6752hs_state *h = i2c_get_clientdata(client);
+       int dist_352, dist_480, dist_720;
+
+       /*
+         FIXME: translate and round width/height into EMPRESS
+         subsample type:
+
+         type   |   PAL   |  NTSC
+         ---------------------------
+         SIF    | 352x288 | 352x240
+         1/2 D1 | 352x576 | 352x480
+         2/3 D1 | 480x576 | 480x480
+         D1     | 720x576 | 720x480
+       */
+
+       dist_352 = abs(f->fmt.pix.width - 352);
+       dist_480 = abs(f->fmt.pix.width - 480);
+       dist_720 = abs(f->fmt.pix.width - 720);
+       if (dist_720 < dist_480) {
+               f->fmt.pix.width = 720;
+               f->fmt.pix.height = 576;
+               h->video_format = SAA6752HS_VF_D1;
+       }
+       else if (dist_480 < dist_352) {
+               f->fmt.pix.width = 480;
+               f->fmt.pix.height = 576;
+               h->video_format = SAA6752HS_VF_2_3_D1;
        }
+       else {
+               f->fmt.pix.width = 352;
+               if (abs(f->fmt.pix.height - 576) <
+                   abs(f->fmt.pix.height - 288)) {
+                       f->fmt.pix.height = 576;
+                       h->video_format = SAA6752HS_VF_1_2_D1;
+               }
+               else {
+                       f->fmt.pix.height = 288;
+                       h->video_format = SAA6752HS_VF_SIF;
+               }
+       }
+}
+
+
+static void saa6752hs_set_params(struct i2c_client* client,
+                                struct v4l2_mpeg_compression* params)
+{
+       struct saa6752hs_state *h = i2c_get_clientdata(client);
+
+       /* check PIDs */
+       if (params->ts_pid_pmt <= MPEG_PID_MAX)
+               h->params.ts_pid_pmt = params->ts_pid_pmt;
+       if (params->ts_pid_pcr <= MPEG_PID_MAX)
+               h->params.ts_pid_pcr = params->ts_pid_pcr;
+       if (params->ts_pid_video <= MPEG_PID_MAX)
+               h->params.ts_pid_video = params->ts_pid_video;
+       if (params->ts_pid_audio <= MPEG_PID_MAX)
+               h->params.ts_pid_audio = params->ts_pid_audio;
+
+       /* check bitrate parameters */
+       if ((params->vi_bitrate.mode == V4L2_BITRATE_CBR) ||
+           (params->vi_bitrate.mode == V4L2_BITRATE_VBR))
+               h->params.vi_bitrate.mode = params->vi_bitrate.mode;
+       if (params->vi_bitrate.mode != V4L2_BITRATE_NONE)
+               h->params.st_bitrate.target = params->st_bitrate.target;
+       if (params->vi_bitrate.mode != V4L2_BITRATE_NONE)
+               h->params.vi_bitrate.target = params->vi_bitrate.target;
+       if (params->vi_bitrate.mode == V4L2_BITRATE_VBR)
+               h->params.vi_bitrate.max = params->vi_bitrate.max;
+       if (params->au_bitrate.mode != V4L2_BITRATE_NONE)
+               h->params.au_bitrate.target = params->au_bitrate.target;
+
+       /* aspect ratio */
+       if (params->vi_aspect_ratio == V4L2_MPEG_ASPECT_4_3 ||
+           params->vi_aspect_ratio == V4L2_MPEG_ASPECT_16_9)
+               h->params.vi_aspect_ratio = params->vi_aspect_ratio;
+
+       /* range checks */
+       if (h->params.st_bitrate.target > MPEG_TOTAL_TARGET_BITRATE_MAX)
+               h->params.st_bitrate.target = MPEG_TOTAL_TARGET_BITRATE_MAX;
+       if (h->params.vi_bitrate.target > MPEG_VIDEO_TARGET_BITRATE_MAX)
+               h->params.vi_bitrate.target = MPEG_VIDEO_TARGET_BITRATE_MAX;
+       if (h->params.vi_bitrate.max > MPEG_VIDEO_MAX_BITRATE_MAX)
+               h->params.vi_bitrate.max = MPEG_VIDEO_MAX_BITRATE_MAX;
+       if (h->params.au_bitrate.target <= 256)
+               h->params.au_bitrate.target = 256;
+       else
+               h->params.au_bitrate.target = 384;
+}
+
+static int saa6752hs_init(struct i2c_client* client)
+{
+       unsigned char buf[9], buf2[4];
+       struct saa6752hs_state *h;
+       u32 crc;
+       unsigned char localPAT[256];
+       unsigned char localPMT[256];
+
+       h = i2c_get_clientdata(client);
 
-       // Set GOP structure {3, 13}
+       // Set video format - must be done first as it resets other settings
+       buf[0] = 0x41;
+       buf[1] = h->video_format;
+       i2c_master_send(client, buf, 2);
+
+        // set bitrate
+        saa6752hs_set_bitrate(client, &h->params);
+
+       // Set GOP structure {3, 13}
        buf[0] = 0x72;
        buf[1] = 0x03;
        buf[2] = 0x0D;
@@ -265,7 +407,7 @@ static int saa6752hs_init(struct i2c_client* client, struct mpeg_params* params)
 
        // Set Output Protocol
        buf[0] = 0xD0;
-       buf[1] = 0x01;
+       buf[1] = 0x81;
        i2c_master_send(client,buf,2);
 
        // Set video output stream format {TS}
@@ -273,25 +415,53 @@ static int saa6752hs_init(struct i2c_client* client, struct mpeg_params* params)
        buf[1] = 0x05;
        i2c_master_send(client,buf,2);
 
-       // Set Audio PID {0x103}
+       /* compute PAT */
+       memcpy(localPAT, PAT, sizeof(PAT));
+       localPAT[17] = 0xe0 | ((h->params.ts_pid_pmt >> 8) & 0x0f);
+       localPAT[18] = h->params.ts_pid_pmt & 0xff;
+       crc = crc32_be(~0, &localPAT[7], sizeof(PAT) - 7 - 4);
+       localPAT[sizeof(PAT) - 4] = (crc >> 24) & 0xFF;
+       localPAT[sizeof(PAT) - 3] = (crc >> 16) & 0xFF;
+       localPAT[sizeof(PAT) - 2] = (crc >> 8) & 0xFF;
+       localPAT[sizeof(PAT) - 1] = crc & 0xFF;
+
+       /* compute PMT */
+       memcpy(localPMT, PMT, sizeof(PMT));
+       localPMT[3] = 0x40 | ((h->params.ts_pid_pmt >> 8) & 0x0f);
+       localPMT[4] = h->params.ts_pid_pmt & 0xff;
+       localPMT[15] = 0xE0 | ((h->params.ts_pid_pcr >> 8) & 0x0F);
+       localPMT[16] = h->params.ts_pid_pcr & 0xFF;
+       localPMT[20] = 0xE0 | ((h->params.ts_pid_video >> 8) & 0x0F);
+       localPMT[21] = h->params.ts_pid_video & 0xFF;
+       localPMT[25] = 0xE0 | ((h->params.ts_pid_audio >> 8) & 0x0F);
+       localPMT[26] = h->params.ts_pid_audio & 0xFF;
+       crc = crc32_be(~0, &localPMT[7], sizeof(PMT) - 7 - 4);
+       localPMT[sizeof(PMT) - 4] = (crc >> 24) & 0xFF;
+       localPMT[sizeof(PMT) - 3] = (crc >> 16) & 0xFF;
+       localPMT[sizeof(PMT) - 2] = (crc >> 8) & 0xFF;
+       localPMT[sizeof(PMT) - 1] = crc & 0xFF;
+
+       // Set Audio PID
        buf[0] = 0xC1;
-       buf[1] = 0x01;
-       buf[2] = 0x03;
+       buf[1] = (h->params.ts_pid_audio >> 8) & 0xFF;
+       buf[2] = h->params.ts_pid_audio & 0xFF;
        i2c_master_send(client,buf,3);
 
-        // setup bitrate settings
-       data = i2c_get_clientdata(client);
-       if (params) {
-               saa6752hs_set_bitrate(client, params);
-               memcpy(data, params, sizeof(struct mpeg_params));
-       } else {
-               // parameters were not supplied. use the previous set
-               saa6752hs_set_bitrate(client, (struct mpeg_params*) data);
-       }
+       // Set Video PID
+       buf[0] = 0xC0;
+       buf[1] = (h->params.ts_pid_video >> 8) & 0xFF;
+       buf[2] = h->params.ts_pid_video & 0xFF;
+       i2c_master_send(client,buf,3);
+
+       // Set PCR PID
+       buf[0] = 0xC4;
+       buf[1] = (h->params.ts_pid_pcr >> 8) & 0xFF;
+       buf[2] = h->params.ts_pid_pcr & 0xFF;
+       i2c_master_send(client,buf,3);
 
        // Send SI tables
-       i2c_master_send(client,PAT,sizeof(PAT));
-       i2c_master_send(client,PMT,sizeof(PMT));
+       i2c_master_send(client,localPAT,sizeof(PAT));
+       i2c_master_send(client,localPMT,sizeof(PMT));
 
        // mute then unmute audio. This removes buzzing artefacts
        buf[0] = 0xa4;
@@ -303,31 +473,56 @@ static int saa6752hs_init(struct i2c_client* client, struct mpeg_params* params)
        // start it going
        saa6752hs_chip_command(client, SAA6752HS_COMMAND_START);
 
+       // readout current state
+       buf[0] = 0xE1;
+       buf[1] = 0xA7;
+       buf[2] = 0xFE;
+       buf[3] = 0x82;
+       buf[4] = 0xB0;
+       i2c_master_send(client, buf, 5);
+       i2c_master_recv(client, buf2, 4);
+
+       // change aspect ratio
+       buf[0] = 0xE0;
+       buf[1] = 0xA7;
+       buf[2] = 0xFE;
+       buf[3] = 0x82;
+       buf[4] = 0xB0;
+       buf[5] = buf2[0];
+       switch(h->params.vi_aspect_ratio) {
+       case V4L2_MPEG_ASPECT_16_9:
+               buf[6] = buf2[1] | 0x40;
+               break;
+       case V4L2_MPEG_ASPECT_4_3:
+       default:
+               buf[6] = buf2[1] & 0xBF;
+               break;
+               break;
+       }
+       buf[7] = buf2[2];
+       buf[8] = buf2[3];
+       i2c_master_send(client, buf, 9);
+
+       // return success
        return 0;
 }
 
 static int saa6752hs_attach(struct i2c_adapter *adap, int addr, int kind)
 {
-       struct i2c_client *client;
-       struct mpeg_params* params;
-
-        client_template.adapter = adap;
-        client_template.addr = addr;
+       struct saa6752hs_state *h;
 
         printk("saa6752hs: chip found @ 0x%x\n", addr<<1);
 
-        if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)))
+        if (NULL == (h = kmalloc(sizeof(*h), GFP_KERNEL)))
                 return -ENOMEM;
-        memcpy(client,&client_template,sizeof(struct i2c_client));
-       strlcpy(client->name, "saa6752hs", sizeof(client->name));
-
-       if (NULL == (params = kmalloc(sizeof(struct mpeg_params), GFP_KERNEL)))
-               return -ENOMEM;
-       memcpy(params,&mpeg_params_template,sizeof(struct mpeg_params));
-       i2c_set_clientdata(client, params);
-
-        i2c_attach_client(client);
-
+       memset(h,0,sizeof(*h));
+       h->client = client_template;
+       h->params = param_defaults;
+       h->client.adapter = adap;
+       h->client.addr = addr;
+
+       i2c_set_clientdata(&h->client, h);
+        i2c_attach_client(&h->client);
        return 0;
 }
 
@@ -340,30 +535,58 @@ static int saa6752hs_probe(struct i2c_adapter *adap)
 
 static int saa6752hs_detach(struct i2c_client *client)
 {
-       void *data;
+       struct saa6752hs_state *h;
 
-       data = i2c_get_clientdata(client);
+       h = i2c_get_clientdata(client);
        i2c_detach_client(client);
-       kfree(data);
-       kfree(client);
+       kfree(h);
        return 0;
 }
 
 static int
 saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg)
 {
-       struct mpeg_params* init_arg = arg;
+       struct saa6752hs_state *h = i2c_get_clientdata(client);
+       struct v4l2_mpeg_compression *params = arg;
+       int err = 0;
 
         switch (cmd) {
-       case MPEG_SETPARAMS:
-               return saa6752hs_init(client, init_arg);
+       case VIDIOC_S_MPEGCOMP:
+               if (NULL == params) {
+                       /* apply settings and start encoder */
+                       saa6752hs_init(client);
+                       break;
+               }
+               saa6752hs_set_params(client, params);
+               /* fall through */
+       case VIDIOC_G_MPEGCOMP:
+               *params = h->params;
+               break;
+       case VIDIOC_G_FMT:
+       {
+           struct v4l2_format *f = arg;
+
+          if (h->video_format == SAA6752HS_VF_UNKNOWN)
+                  h->video_format = SAA6752HS_VF_D1;
+          f->fmt.pix.width =
+                  v4l2_format_table[h->video_format].fmt.pix.width;
+          f->fmt.pix.height =
+                  v4l2_format_table[h->video_format].fmt.pix.height;
+          break ;
+       }
+       case VIDIOC_S_FMT:
+       {
+               struct v4l2_format *f = arg;
 
+               saa6752hs_set_subsampling(client, f);
+               break;
+       }
        default:
                /* nothing */
                break;
        }
 
-       return 0;
+       return err;
 }
 
 /* ----------------------------------------------------------------------- */
@@ -380,7 +603,7 @@ static struct i2c_driver driver = {
 
 static struct i2c_client client_template =
 {
-       I2C_DEVNAME("(saa6752hs unset)"),
+       I2C_DEVNAME("saa6752hs"),
        .flags      = I2C_CLIENT_ALLOW_USE,
         .driver     = &driver,
 };